From b9e5413ec5b67fba10ca382fdecaafac778a1bb2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 10 May 2018 17:55:21 +0200 Subject: [PATCH 01/12] Make ValueClass operations take Symbols, not SymDenotations --- .../tools/dotc/transform/TreeExtractors.scala | 4 ++-- .../tools/dotc/transform/ValueClasses.scala | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeExtractors.scala b/compiler/src/dotty/tools/dotc/transform/TreeExtractors.scala index 7a5c5df9db98..680f21bff1ac 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeExtractors.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeExtractors.scala @@ -36,8 +36,8 @@ object TreeExtractors { object ValueClassUnbox { def unapply(t: Tree)(implicit ctx: Context): Option[Tree] = t match { case Apply(sel @ Select(ref, _), Nil) => - val d = ref.tpe.widenDealias.typeSymbol.denot - if (isDerivedValueClass(d) && (sel.symbol eq valueClassUnbox(d.asClass))) { + val sym = ref.tpe.widenDealias.typeSymbol + if (isDerivedValueClass(sym) && (sel.symbol eq valueClassUnbox(sym.asClass))) { Some(ref) } else None diff --git a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala index a186ec0feee0..120424204274 100644 --- a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala +++ b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala @@ -4,7 +4,6 @@ package transform import core._ import Types._ import Symbols._ -import SymDenotations._ import Contexts._ import Flags._ import StdNames._ @@ -13,7 +12,8 @@ import SymUtils._ /** Methods that apply to user-defined value classes */ object ValueClasses { - def isDerivedValueClass(d: SymDenotation)(implicit ctx: Context) = { + def isDerivedValueClass(sym: Symbol)(implicit ctx: Context): Boolean = { + val d = sym.denot !ctx.settings.XnoValueClasses.value && !d.isRefinementClass && d.isValueClass && @@ -33,27 +33,27 @@ object ValueClasses { } /** The member of a derived value class that unboxes it. */ - def valueClassUnbox(d: ClassDenotation)(implicit ctx: Context): Symbol = + def valueClassUnbox(cls: ClassSymbol)(implicit ctx: Context): Symbol = // (info.decl(nme.unbox)).orElse(...) uncomment once we accept unbox methods - d.classInfo.decls.find(_.is(ParamAccessor)) + cls.classInfo.decls.find(_.is(ParamAccessor)) /** For a value class `d`, this returns the synthetic cast from the underlying type to * ErasedValueType defined in the companion module. This method is added to the module * and further described in [[ExtensionMethods]]. */ - def u2evt(d: ClassDenotation)(implicit ctx: Context): Symbol = - d.linkedClass.info.decl(nme.U2EVT).symbol + def u2evt(cls: ClassSymbol)(implicit ctx: Context): Symbol = + cls.linkedClass.info.decl(nme.U2EVT).symbol /** For a value class `d`, this returns the synthetic cast from ErasedValueType to the * underlying type defined in the companion module. This method is added to the module * and further described in [[ExtensionMethods]]. */ - def evt2u(d: ClassDenotation)(implicit ctx: Context): Symbol = - d.linkedClass.info.decl(nme.EVT2U).symbol + def evt2u(cls: ClassSymbol)(implicit ctx: Context): Symbol = + cls.linkedClass.info.decl(nme.EVT2U).symbol /** The unboxed type that underlies a derived value class */ - def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type = - valueClassUnbox(d).info.resultType + def underlyingOfValueClass(sym: ClassSymbol)(implicit ctx: Context): Type = + valueClassUnbox(sym).info.resultType /** Whether a value class wraps itself */ def isCyclic(cls: ClassSymbol)(implicit ctx: Context): Boolean = { From b8d05a9ce2af092513c32c44929ad7801e88c7db Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 May 2018 15:11:58 +0200 Subject: [PATCH 02/12] Eliminate redundant "symbol" calls on Symbols. Everyone of these is a roundtrip `toDenot(stm).symbol`. --- .../tools/backend/jvm/DottyBackendInterface.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- .../src/dotty/tools/dotc/core/Definitions.scala | 14 +++++--------- .../src/dotty/tools/dotc/core/SymDenotations.scala | 8 ++++---- compiler/src/dotty/tools/dotc/core/Symbols.scala | 8 +++++++- .../dotc/core/classfile/ClassfileParser.scala | 2 +- .../dotty/tools/dotc/interactive/Interactive.scala | 4 ++-- .../dotty/tools/dotc/transform/LambdaLift.scala | 2 +- .../src/dotty/tools/dotc/transform/Mixin.scala | 2 +- .../src/dotty/tools/dotc/typer/Inferencing.scala | 2 +- 10 files changed, 25 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index c3ad297c860b..45ac2570e643 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -842,7 +842,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def superInterfaces: List[Symbol] = { val directlyInheritedTraits = decorateSymbol(sym).directlyInheritedTraits val directlyInheritedTraitsSet = directlyInheritedTraits.toSet - val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.symbol.asClass.baseClasses.drop(1)).toSet + val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.asClass.baseClasses.drop(1)).toSet val superCalls = superCallsMap.getOrElse(sym, Set.empty) val additional = (superCalls -- directlyInheritedTraitsSet).filter(_.is(Flags.Trait)) // if (additional.nonEmpty) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c7017a8f547c..28cd05f3dd5d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1094,9 +1094,9 @@ object desugar { Literal(Constant(scala.Symbol(str))) case Quote(expr) => if (expr.isType) - TypeApply(ref(defn.QuotedType_applyR), List(expr)) + TypeApply(ref(defn.QuotedType_apply.termRef), List(expr)) else - Apply(ref(defn.QuotedExpr_applyR), expr) + Apply(ref(defn.QuotedExpr_apply.termRef), expr) case InterpolatedString(id, segments) => val strs = segments map { case ts: Thicket => ts.trees.head diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index c87ff6b059ba..b436a12b98c6 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -625,7 +625,7 @@ class Definitions { lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix) def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol lazy val LanguageModuleRef = ctx.requiredModule("scala.language") - def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass + def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.moduleClass.asClass lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl") lazy val SelectableType: TypeRef = ctx.requiredClassRef("scala.Selectable") @@ -638,16 +638,12 @@ class Definitions { lazy val QuotedExprModuleType = ctx.requiredModuleRef("scala.quoted.Expr") def QuotedExprModule(implicit ctx: Context) = QuotedExprModuleType.symbol - lazy val QuotedExpr_applyR = QuotedExprModule.requiredMethodRef(nme.apply) - def QuotedExpr_apply(implicit ctx: Context) = QuotedExpr_applyR.symbol - - lazy val QuotedExpr_spliceR = QuotedExprClass.requiredMethod(nme.UNARY_~) - def QuotedExpr_~(implicit ctx: Context) = QuotedExpr_spliceR.symbol - lazy val QuotedExpr_runR = QuotedExprClass.requiredMethodRef(nme.run) - def QuotedExpr_run(implicit ctx: Context) = QuotedExpr_runR.symbol + def QuotedExpr_apply(implicit ctx: Context) = QuotedExprModule.requiredMethod(nme.apply) + def QuotedExpr_~(implicit ctx: Context) = QuotedExprClass.requiredMethod(nme.UNARY_~) + def QuotedExpr_run(implicit ctx: Context) =QuotedExprClass.requiredMethodRef(nme.run) lazy val QuotedExprsModule = ctx.requiredModule("scala.quoted.Exprs") - def QuotedExprsClass(implicit ctx: Context) = QuotedExprsModule.symbol.asClass + def QuotedExprsClass(implicit ctx: Context) = QuotedExprsModule.asClass lazy val QuotedTypeType = ctx.requiredClassRef("scala.quoted.Type") def QuotedTypeClass(implicit ctx: Context) = QuotedTypeType.symbol.asClass diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a8a53da9ffa5..00f735a9eb95 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -580,13 +580,13 @@ object SymDenotations { myFlags.is(ModuleClass) && (myFlags.is(PackageClass) || isStatic) /** Is this denotation defined in the same scope and compilation unit as that symbol? */ - final def isCoDefinedWith(that: Symbol)(implicit ctx: Context) = - (this.effectiveOwner == that.effectiveOwner) && + final def isCoDefinedWith(other: Symbol)(implicit ctx: Context) = + (this.effectiveOwner == other.effectiveOwner) && ( !(this.effectiveOwner is PackageClass) - || this.unforcedIsAbsent || that.unforcedIsAbsent + || this.unforcedIsAbsent || other.unforcedIsAbsent || { // check if they are defined in the same file(or a jar) val thisFile = this.symbol.associatedFile - val thatFile = that.symbol.associatedFile + val thatFile = other.associatedFile ( thisFile == null || thatFile == null || thisFile.path == thatFile.path // Cheap possibly wrong check, then expensive normalization diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 7cae7b1822ab..50d48d3256e7 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -564,7 +564,7 @@ object Symbols { * Overridden in ClassSymbol */ def associatedFile(implicit ctx: Context): AbstractFile = - if (lastDenot == null) null else lastDenot.topLevelClass.symbol.associatedFile + if (lastDenot == null) null else lastDenot.topLevelClass.associatedFile /** The class file from which this class was generated, null if not applicable. */ final def binaryFile(implicit ctx: Context): AbstractFile = { @@ -572,6 +572,12 @@ object Symbols { if (file != null && file.extension == "class") file else null } + /** A trap to avoid calling x.symbol on something that is already a symbol. + * This would be expanded to `toDenot(x).symbol` which is guaraneteed to be + * the same as `x` + */ + final def symbol: Nothing = unsupported("symbol") + /** The source file from which this class was generated, null if not applicable. */ final def sourceFile(implicit ctx: Context): AbstractFile = { val file = associatedFile diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 86d7866041ad..2379ddfc610b 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -172,7 +172,7 @@ class ClassfileParser( setClassInfo(classRoot, classInfo) setClassInfo(moduleRoot, staticInfo) } else if (result == Some(NoEmbedded)) { - for (sym <- List(moduleRoot.sourceModule.symbol, moduleRoot.symbol, classRoot.symbol)) { + for (sym <- List(moduleRoot.sourceModule, moduleRoot.symbol, classRoot.symbol)) { classRoot.owner.asClass.delete(sym) if (classRoot.owner == defn.ScalaShadowingPackageClass) { // Symbols in scalaShadowing are also added to scala diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index 5cd531338e6d..5c17f62a79da 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -231,8 +231,8 @@ object Interactive { val boundaryCtx = ctx.withOwner(boundary) def exclude(sym: Symbol) = sym.isAbsent || sym.is(Synthetic) || sym.is(Artifact) def addMember(name: Name, buf: mutable.Buffer[SingleDenotation]): Unit = - buf ++= prefix.member(name).altsWith(d => - !exclude(d) && d.symbol.isAccessibleFrom(prefix)(boundaryCtx)) + buf ++= prefix.member(name).altsWith(sym => + !exclude(sym) && sym.isAccessibleFrom(prefix)(boundaryCtx)) prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList } else Nil diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index 6cd1ad06156a..f7ffb7289568 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -190,7 +190,7 @@ object LambdaLift { def traverse(tree: Tree)(implicit ctx: Context) = try { //debug val sym = tree.symbol - def enclosure = ctx.owner.enclosingMethod.symbol + def enclosure = ctx.owner.enclosingMethod def narrowTo(thisClass: ClassSymbol) = { val enclMethod = enclosure diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 437a8a45af3c..04ececdc2b1b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -132,7 +132,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => initName, Protected | Synthetic | Method, sym.info, - coord = sym.symbol.coord).enteredAfter(thisPhase)) + coord = sym.coord).enteredAfter(thisPhase)) } }.asTerm diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 9b6c128d023b..f0611878b666 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -279,7 +279,7 @@ object Inferencing { case tp: TypeRef => val companion = tp.classSymbol.companionModule if (companion.exists) - companion.termRef.asSeenFrom(tp.prefix, companion.symbol.owner) + companion.termRef.asSeenFrom(tp.prefix, companion.owner) else NoType case _ => NoType } From 5ce3021074dcecabab63887e230839c8c6f4769d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 May 2018 16:09:33 +0200 Subject: [PATCH 03/12] A better honeytrap for Symbol#symbol Managed to find two more violations at compile time. --- compiler/src/dotty/tools/dotc/core/Symbols.scala | 7 ++++--- .../tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 2 +- .../tools/dotc/transform/localopt/InlineLocalObjects.scala | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 50d48d3256e7..51ad8507f4aa 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -574,9 +574,11 @@ object Symbols { /** A trap to avoid calling x.symbol on something that is already a symbol. * This would be expanded to `toDenot(x).symbol` which is guaraneteed to be - * the same as `x` + * the same as `x`. + * With the given setup, all such calls will give implicit-not found errors */ - final def symbol: Nothing = unsupported("symbol") + final def symbol(implicit ev: DontUseSymbolOnSymbol): Nothing = unsupported("symbol") + type DontUseSymbolOnSymbol /** The source file from which this class was generated, null if not applicable. */ final def sourceFile(implicit ctx: Context): AbstractFile = { @@ -803,5 +805,4 @@ object Symbols { @inline def newMutableSymbolMap[T]: MutableSymbolMap[T] = new MutableSymbolMap(new java.util.IdentityHashMap[Symbol, T]()) - } diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index f7dbda218e0e..29a311a16f1f 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -351,7 +351,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val owner = if (atEnd) loadingMirror.RootClass else readSymbolRef() def adjust(denot: Denotation) = { - val denot1 = denot.disambiguate(d => p(d.symbol)) + val denot1 = denot.disambiguate(p) val sym = denot1.symbol if (denot.exists && !denot1.exists) { // !!!DEBUG val alts = denot.alternatives map (d => d + ":" + d.info + "/" + d.signature) diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/InlineLocalObjects.scala b/compiler/src/dotty/tools/dotc/transform/localopt/InlineLocalObjects.scala index 8134764fd3cf..64bff5521381 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/InlineLocalObjects.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/InlineLocalObjects.scala @@ -92,7 +92,7 @@ class InlineLocalObjects(val simplifyPhase: Simplify) extends Optimisation { case t @ NewCaseClassValDef(fun, args) if newFieldsMapping.contains(t.symbol) => val newFields = newFieldsMapping(t.symbol).values.toList val newFieldsDefs = newFields.zip(args).map { case (nf, arg) => - val rhs = arg.changeOwnerAfter(t.symbol, nf.symbol, simplifyPhase) + val rhs = arg.changeOwnerAfter(t.symbol, nf, simplifyPhase) ValDef(nf.asTerm, rhs) } val recreate = cpy.ValDef(t)(rhs = fun.appliedToArgs(newFields.map(x => ref(x)))) From 62017b9df80b132107dd8244ad6e5742da4ae521 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 May 2018 15:35:32 +0200 Subject: [PATCH 04/12] Add counters for some Symbol and Denotation operations and types --- compiler/src/dotty/tools/dotc/core/Denotations.scala | 6 +++++- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 ++ compiler/src/dotty/tools/dotc/core/Symbols.scala | 7 ++++++- compiler/src/dotty/tools/dotc/core/Types.scala | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 54993ce45cf6..4732ab132f2d 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -21,6 +21,7 @@ import printing.Printer import io.AbstractFile import config.Config import util.common._ +import util.Stats import collection.mutable.ListBuffer import Decorators.SymbolIteratorDecorator @@ -174,6 +175,7 @@ object Denotations { abstract class Denotation(val symbol: Symbol) extends PreDenotation with printing.Showable { type AsSeenFromResult <: Denotation + Stats.record("Denotation") /** The type info of the denotation, exists only for non-overloaded denotations */ def info(implicit ctx: Context): Type @@ -1139,6 +1141,8 @@ object Denotations { def denot1: PreDenotation def denot2: PreDenotation + Stats.record("MultiPreDenotation") + assert(denot1.exists && denot2.exists, s"Union of non-existing denotations ($denot1) and ($denot2)") def first = denot1.first def last = denot2.last @@ -1290,7 +1294,7 @@ object Denotations { /** An exception for accessing symbols that are no longer valid in current run */ class StaleSymbol(msg: => String) extends Exception { - util.Stats.record("stale symbol") + Stats.record("stale symbol") override def getMessage() = msg } } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 00f735a9eb95..970e488d0db9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -127,6 +127,8 @@ object SymDenotations { //assert(symbol.id != 4940, name) + Stats.record("SymDenotation") + override def hasUniqueSym: Boolean = exists /** Debug only diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 51ad8507f4aa..269c30792331 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -29,7 +29,7 @@ import Denotations.{ Denotation, SingleDenotation, MultiDenotation } import collection.mutable import io.AbstractFile import language.implicitConversions -import util.{NoSource, DotClass, Property} +import util.{NoSource, DotClass, Property, Stats} import scala.collection.JavaConverters._ import config.Printers.typr @@ -416,6 +416,8 @@ object Symbols { //assert(id != 723) + Stats.record("Symbol") + def coord: Coord = myCoord /** Set the coordinate of this class, this is only useful when the coordinate is * not known at symbol creation. This is the case for root symbols @@ -442,12 +444,14 @@ object Symbols { /** The current denotation of this symbol */ final def denot(implicit ctx: Context): SymDenotation = { + Stats.record("Symbol.denot") val lastd = lastDenot if (checkedPeriod == ctx.period) lastd else computeDenot(lastd) } private def computeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = { + Stats.record("Symbol.computeDenot") val now = ctx.period checkedPeriod = now if (lastd.validFor contains now) lastd else recomputeDenot(lastd) @@ -455,6 +459,7 @@ object Symbols { /** Overridden in NoSymbol */ protected def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context) = { + Stats.record("Symbol.recomputeDenot") val newd = lastd.current.asInstanceOf[SymDenotation] lastDenot = newd newd diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3e85582bef81..c3e461678ed3 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1690,6 +1690,7 @@ object Types { /** The denotation currently denoted by this type */ final def denot(implicit ctx: Context): Denotation = { + record("NamedType.denot") val now = ctx.period // Even if checkedPeriod == now we still need to recheck lastDenotation.validFor // as it may have been mutated by SymDenotation#installAfter From c5a424cd44e2ea19c8df64731c0ec6e1ff45a1c7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 May 2018 15:59:19 +0200 Subject: [PATCH 05/12] Add caches for hot SymDenotations fields to Symbol --- .../tools/dotc/core/SymDenotations.scala | 5 +++- .../src/dotty/tools/dotc/core/Symbols.scala | 23 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 970e488d0db9..d9ca7827b261 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -160,8 +160,10 @@ object SymDenotations { private def adaptFlags(flags: FlagSet) = if (isType) flags.toTypeFlags else flags.toTermFlags /** Update the flag set */ - final def flags_=(flags: FlagSet): Unit = + final def flags_=(flags: FlagSet): Unit = { myFlags = adaptFlags(flags) + symbol.updateFlagsCache(this, myFlags) + } /** Set given flags(s) of this denotation */ final def setFlag(flags: FlagSet): Unit = { myFlags |= flags } @@ -259,6 +261,7 @@ object SymDenotations { */ if (Config.checkNoSkolemsInInfo) assertNoSkolems(tp) myInfo = tp + symbol.updateInfoCache(this, tp) } /** The name, except diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 269c30792331..689955896d40 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -434,11 +434,30 @@ object Symbols { private[this] var lastDenot: SymDenotation = _ private[this] var checkedPeriod: Period = Nowhere + private[this] var cachedName: Name = _ + private[this] var cachedOwner: Symbol = _ + private[this] var cachedInfo: Type = _ + private[this] var cachedFlags: FlagSet = _ + private[core] def invalidateDenotCache() = { checkedPeriod = Nowhere } + private[core] def updateInfoCache(d: SymDenotation, info: Type) = + if (lastDenot `eq` d) cachedInfo = info + + private[core] def updateFlagsCache(d: SymDenotation, flags: FlagSet) = + if (lastDenot `eq` d) cachedFlags = flags + + private[this] def setLastDenot(d: SymDenotation) = { + lastDenot = d + cachedName = d.name + cachedOwner = d.maybeOwner + cachedInfo = d.infoOrCompleter + cachedFlags = d.flagsUNSAFE + } + /** Set the denotation of this symbol */ private[core] def denot_=(d: SymDenotation) = { - lastDenot = d + setLastDenot(d) checkedPeriod = Nowhere } @@ -461,7 +480,7 @@ object Symbols { protected def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context) = { Stats.record("Symbol.recomputeDenot") val newd = lastd.current.asInstanceOf[SymDenotation] - lastDenot = newd + setLastDenot(newd) newd } From bad5db5ad7e149ec386be75966b4cc08ba28d583 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 May 2018 19:23:36 +0200 Subject: [PATCH 06/12] Cache hot fields in Symbols, and some refactorings --- .../backend/jvm/CollectEntryPoints.scala | 10 +- .../backend/jvm/DottyBackendInterface.scala | 48 ++--- .../dotty/tools/dotc/core/Denotations.scala | 29 --- .../tools/dotc/core/SymDenotations.scala | 18 +- .../src/dotty/tools/dotc/core/Symbols.scala | 165 +++++++++++++++++- 5 files changed, 200 insertions(+), 70 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala index 8e7cfc688724..7965a8f4c880 100644 --- a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala @@ -61,11 +61,11 @@ object CollectEntryPoints{ val StringType = d.StringType // The given class has a main method. def hasJavaMainMethod(sym: Symbol): Boolean = - (toDenot(sym).info member nme.main).alternatives exists(x => isJavaMainMethod(x.symbol)) + (sym.info member nme.main).alternatives exists(x => isJavaMainMethod(x.symbol)) def fail(msg: String, pos: Position = sym.pos) = { ctx.warning( sym.name + - s" has a main method with parameter type Array[String], but ${toDenot(sym).fullName} will not be a runnable program.\n Reason: $msg", + s" has a main method with parameter type Array[String], but ${sym.fullName} will not be a runnable program.\n Reason: $msg", sourcePos(sym.pos) // TODO: make this next claim true, if possible // by generating valid main methods as static in module classes @@ -77,7 +77,7 @@ object CollectEntryPoints{ def failNoForwarder(msg: String) = { fail(s"$msg, which means no static forwarder can be generated.\n") } - val possibles = if (sym.flags is Flags.Module) (toDenot(sym).info nonPrivateMember nme.main).alternatives else Nil + val possibles = if (sym.flags is Flags.Module) (sym.info nonPrivateMember nme.main).alternatives else Nil val hasApproximate = possibles exists { m => m.info match { case MethodTpe(_, p :: Nil, _) => p.typeSymbol == defn.ArrayClass @@ -94,7 +94,7 @@ object CollectEntryPoints{ if (hasJavaMainMethod(companion)) failNoForwarder("companion contains its own main method") - else if (toDenot(companion).info.member(nme.main) != NoDenotation) + else if (companion.info.member(nme.main) != NoDenotation) // this is only because forwarders aren't smart enough yet failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") else if (companion.flags is Flags.Trait) @@ -103,7 +103,7 @@ object CollectEntryPoints{ // attempts to be java main methods. else (possibles exists(x=> isJavaMainMethod(x.symbol))) || { possibles exists { m => - toDenot(m.symbol).info match { + m.symbol.info match { case t: PolyType => fail("main methods cannot be generic.") case MethodTpe(paramNames, paramTypes, resultType) => diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 45ac2570e643..a4e2a49464fd 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -143,7 +143,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma val externalEqualsNumNum: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum) val externalEqualsNumChar: Symbol = NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private val externalEqualsNumObject: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject) - val externalEquals: Symbol = defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol + val externalEquals: Symbol = defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(_.info.firstParamTypes.size == 2).symbol val MaxFunctionArity: Int = Definitions.MaxImplementedFunctionArity val FunctionClass: Array[Symbol] = defn.FunctionClassPerRun() val AbstractFunctionClass: Array[Symbol] = defn.AbstractFunctionClassPerRun() @@ -169,9 +169,9 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } def isBox(sym: Symbol): Boolean = - Erasure.Boxing.isBox(sym) && sym.denot.owner != defn.UnitModuleClass + Erasure.Boxing.isBox(sym) && sym.owner != defn.UnitModuleClass def isUnbox(sym: Symbol): Boolean = - Erasure.Boxing.isUnbox(sym) && sym.denot.owner != defn.UnitModuleClass + Erasure.Boxing.isUnbox(sym) && sym.owner != defn.UnitModuleClass val primitives: Primitives = new Primitives { val primitives = new DottyPrimitives(ctx) @@ -256,16 +256,16 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma av.visitEnum(name, edesc, evalue) } case t: TypeApply if (t.fun.symbol == Predef_classOf) => - av.visit(name, t.args.head.tpe.classSymbol.denot.info.toTypeKind(bcodeStore)(innerClasesStore).toASMType) + av.visit(name, t.args.head.tpe.classSymbol.info.toTypeKind(bcodeStore)(innerClasesStore).toASMType) case t: tpd.Select => - if (t.symbol.denot.owner.is(Flags.JavaEnum)) { + if (t.symbol.owner.is(Flags.JavaEnum)) { val edesc = innerClasesStore.typeDescriptor(t.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class. val evalue = t.symbol.name.mangledString // value the actual enumeration value. av.visitEnum(name, edesc, evalue) } else { - // println(i"not an enum: ${t.symbol} / ${t.symbol.denot.owner} / ${t.symbol.denot.owner.isTerm} / ${t.symbol.denot.owner.flags}") - assert(toDenot(t.symbol).name.is(DefaultGetterName), - s"${toDenot(t.symbol).name.debugString}") // this should be default getter. do not emmit. + // println(i"not an enum: ${t.symbol} / ${t.symbol.owner} / ${t.symbol.owner.isTerm} / ${t.symbol.owner.flags}") + assert(t.symbol.name.is(DefaultGetterName), + s"${t.symbol.name.debugString}") // this should be default getter. do not emmit. } case t: SeqLiteral => val arrAnnotV: AnnotationVisitor = av.visitArray(name) @@ -273,7 +273,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma arrAnnotV.visitEnd() case Apply(fun, args) if fun.symbol == defn.ArrayClass.primaryConstructor || - toDenot(fun.symbol).owner == defn.ArrayClass.linkedClass && fun.symbol.name == nme_apply => + fun.symbol.owner == defn.ArrayClass.linkedClass && fun.symbol.name == nme_apply => val arrAnnotV: AnnotationVisitor = av.visitArray(name) var actualArgs = if (fun.tpe.isImplicitMethod) { @@ -304,7 +304,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. */ case t @ Apply(constr, args) if t.tpe.derivesFrom(JavaAnnotationClass) => - val typ = t.tpe.classSymbol.denot.info + val typ = t.tpe.classSymbol.info val assocs = assocsFromApply(t) val desc = innerClasesStore.typeDescriptor(typ.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the nested annotation class val nestedVisitor = av.visitAnnotation(name, desc) @@ -521,7 +521,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma if (!valid) { ctx.error( - i"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName} + i"""|compiler bug: created invalid generic signature for $sym in ${sym.owner.showFullName} |signature: $sig |if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues """.trim, sym.pos) @@ -540,7 +540,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def getGenericSignature(sym: Symbol, owner: Symbol): String = { ctx.atPhase(ctx.erasurePhase) { implicit ctx => val memberTpe = - if (sym.is(Flags.Method)) sym.denot.info + if (sym.is(Flags.Method)) sym.info else owner.denot.thisType.memberInfo(sym) getGenericSignature(sym, owner, memberTpe).orNull } @@ -555,14 +555,14 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma val memberTpe = ctx.atPhase(ctx.erasurePhase) { implicit ctx => moduleClass.denot.thisType.memberInfo(sym) } val erasedMemberType = TypeErasure.erasure(memberTpe) - if (erasedMemberType =:= sym.denot.info) + if (erasedMemberType =:= sym.info) getGenericSignature(sym, moduleClass, memberTpe).orNull else null } private def getGenericSignature(sym: Symbol, owner: Symbol, memberTpe: Type)(implicit ctx: Context): Option[String] = if (needsGenericSignature(sym)) { - val erasedTypeSym = sym.denot.info.typeSymbol + val erasedTypeSym = sym.info.typeSymbol if (erasedTypeSym.isPrimitiveValueClass) { None } else { @@ -655,7 +655,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def fullName(sep: Char): String = sym.showFullName def fullName: String = sym.showFullName def simpleName: Name = sym.name - def javaSimpleName: String = toDenot(sym).name.mangledString // addModuleSuffix(simpleName.dropLocal) + def javaSimpleName: String = sym.name.mangledString // addModuleSuffix(simpleName.dropLocal) def javaBinaryName: String = javaClassName.replace('.', '/') // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) def javaClassName: String = toDenot(sym).fullName.mangledString // addModuleSuffix(fullNameInternal('.')).toString def name: Name = sym.name @@ -665,8 +665,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } // types - def info: Type = toDenot(sym).info - def tpe: Type = toDenot(sym).info // todo whats the differentce between tpe and info? + def info: Type = sym.info + def tpe: Type = sym.info // todo whats the differentce between tpe and info? def thisType: Type = toDenot(sym).thisType // tests @@ -746,7 +746,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma // navigation - def owner: Symbol = toDenot(sym).owner + def owner: Symbol = sym.owner def rawowner: Symbol = { originalOwner } @@ -760,7 +760,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma val r = toDenot(sym)(shiftedContext).maybeOwner.lexicallyEnclosingClass(shiftedContext) r } else NoSymbol - def parentSymbols: List[Symbol] = toDenot(sym).info.parents.map(_.typeSymbol) + def parentSymbols: List[Symbol] = sym.info.parents.map(_.typeSymbol) def superClass: Symbol = { val t = toDenot(sym).asClass.superClass if (t.exists) t @@ -803,7 +803,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma private def definedClasses(phase: Phase) = if (sym.isDefinedInCurrentRun) ctx.atPhase(phase) { implicit ctx => - toDenot(sym).info.decls.filter(_.isClass) + sym.info.decls.filter(_.isClass) } else Nil @@ -815,10 +815,10 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma else Nil } def fieldSymbols: List[Symbol] = { - toDenot(sym).info.decls.filter(p => p.isTerm && !p.is(Flags.Method)) + sym.info.decls.filter(p => p.isTerm && !p.is(Flags.Method)) } def methodSymbols: List[Symbol] = - for (f <- toDenot(sym).info.decls.toList if f.isMethod && f.isTerm && !f.isModule) yield f + for (f <- sym.info.decls.toList if f.isMethod && f.isTerm && !f.isModule) yield f def serialVUID: Option[Long] = None @@ -856,7 +856,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma */ def isTopLevelModuleClass: Boolean = sym.isModuleClass && ctx.atPhase(ctx.flattenPhase) { implicit ctx => - toDenot(sym).owner.is(Flags.PackageClass) + sym.owner.is(Flags.PackageClass) } /** @@ -874,7 +874,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def addRemoteRemoteExceptionAnnotation: Unit = () def samMethod(): Symbol = - toDenot(sym).info.abstractTermMembers.headOption.getOrElse(toDenot(sym).info.member(nme.apply)).symbol + sym.info.abstractTermMembers.headOption.getOrElse(sym.info.member(nme.apply)).symbol } diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 4732ab132f2d..4ace9fed3549 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -289,35 +289,6 @@ object Denotations { denot.symbol } - def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(_ is Method).asTerm - def requiredMethodRef(name: PreName)(implicit ctx: Context): TermRef = - requiredMethod(name).termRef - - def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = { - info.member(name.toTermName).requiredSymbol { x => - (x is Method) && { - x.info.paramInfoss match { - case paramInfos :: Nil => paramInfos.corresponds(argTypes)(_ =:= _) - case _ => false - } - } - }.asTerm - } - def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef = - requiredMethod(name, argTypes).termRef - - def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol = - info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm - def requiredValueRef(name: PreName)(implicit ctx: Context): TermRef = - requiredValue(name).termRef - - def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = - info.member(name.toTypeName).requiredSymbol(_.isClass).asClass - - def requiredType(name: PreName)(implicit ctx: Context): TypeSymbol = - info.member(name.toTypeName).requiredSymbol(_.isType).asType - /** The alternative of this denotation that has a type matching `targetType` when seen * as a member of type `site`, `NoDenotation` if none exists. */ diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index d9ca7827b261..2f8b864aa35d 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -166,10 +166,16 @@ object SymDenotations { } /** Set given flags(s) of this denotation */ - final def setFlag(flags: FlagSet): Unit = { myFlags |= flags } + final def setFlag(flags: FlagSet): Unit = { + myFlags |= flags + symbol.updateFlagsCache(this, myFlags) + } /** Unset given flags(s) of this denotation */ - final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags } + final def resetFlag(flags: FlagSet): Unit = { + myFlags &~= flags + symbol.updateFlagsCache(this, myFlags) + } /** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */ final def setNoInitsFlags(flags: FlagSet): Unit = { @@ -228,7 +234,7 @@ object SymDenotations { indent += 1 if (myFlags is Touched) throw CyclicReference(this) - myFlags |= Touched + setFlag(Touched) // completions.println(s"completing ${this.debugString}") try completer.complete(this)(ctx.withPhase(validFor.firstPhaseId)) @@ -244,7 +250,7 @@ object SymDenotations { } else { if (myFlags is Touched) throw CyclicReference(this) - myFlags |= Touched + setFlag(Touched) completer.complete(this)(ctx.withPhase(validFor.firstPhaseId)) } @@ -452,8 +458,10 @@ object SymDenotations { def isError: Boolean = false /** Make denotation not exist */ - final def markAbsent(): Unit = + final def markAbsent(): Unit = { myInfo = NoType + symbol.updateInfoCache(this, NoType) + } /** Is symbol known to not exist, or potentially not completed yet? */ final def unforcedIsAbsent(implicit ctx: Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 689955896d40..4a521cdea6ea 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -580,9 +580,6 @@ object Symbols { /** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */ def filter(p: Symbol => Boolean): Symbol = if (p(this)) this else NoSymbol - /** The current name of this symbol */ - final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName] - /** The source or class file from which this class or * the class containing this symbol was generated, null if not applicable. * Overridden in ClassSymbol @@ -630,14 +627,168 @@ object Symbols { def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition // ParamInfo types and methods - def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) - def paramName(implicit ctx: Context) = name.asInstanceOf[ThisName] - def paramInfo(implicit ctx: Context) = denot.info + def isTypeParam(implicit ctx: Context) = is(TypeParam) + def paramName(implicit ctx: Context) = name + def paramInfo(implicit ctx: Context) = info def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this) - def paramInfoOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter + def paramInfoOrCompleter(implicit ctx: Context): Type = infoOrCompleter def paramVariance(implicit ctx: Context) = denot.variance def paramRef(implicit ctx: Context) = denot.typeRef +// -------- Cached SymDenotation facades ----------------------------------------------- + + private def ensureUpToDate()(implicit ctx: Context) = + if (checkedPeriod != ctx.period) computeDenot(lastDenot) + + /** The current name of this symbol */ + final def name(implicit ctx: Context): ThisName = { + Stats.record("Symbol.name") + ensureUpToDate() + cachedName.asInstanceOf[ThisName] + } + + /** The current name of this symbol */ + final def maybeOwner(implicit ctx: Context): Symbol = { + Stats.record("Symbol.owner") + ensureUpToDate() + cachedOwner + } + + def owner(implicit ctx: Context): Symbol = maybeOwner + + /** The symbol is completed: info is not a lazy type and attributes have defined values */ + final def isCompleted(implicit ctx: Context): Boolean = { + ensureUpToDate() + !cachedInfo.isInstanceOf[LazyType] + } + + /** The denotation is in train of being completed */ + final def isCompleting(implicit ctx: Context): Boolean = { + ensureUpToDate() + cachedInfo.isInstanceOf[LazyType] && cachedFlags.is(Touched) + } + + /** Make sure this denotation of this symbol is completed */ + final def ensureCompleted()(implicit ctx: Context): Unit = { + ensureUpToDate() + if (cachedInfo.isInstanceOf[LazyType]) denot.ensureCompleted() + } + + final def infoOrCompleter(implicit ctx: Context): Type = { + Stats.record("Symbol.info") + ensureUpToDate() + cachedInfo + } + + final def info(implicit ctx: Context): Type = { + val tp = infoOrCompleter + if (tp.isInstanceOf[LazyType]) denot.info else tp + } + + final private[dotc] def info_=(tp: Type)(implicit ctx: Context) = denot.info_=(tp) + + final def flags(implicit ctx: Context): FlagSet = { + ensureCompleted() + val res = cachedFlags + assert(res == denot.flags, i"was: ${denot.flags}, now: $res") + res + } + + final def flags_=(flags: FlagSet)(implicit ctx: Context): Unit = denot.flags_=(flags) + + private def isCurrent(fs: FlagSet) = + fs <= ( + if (cachedInfo.isInstanceOf[SymbolLoader]) FromStartFlags + else AfterLoadFlags) + + final def is(fs: FlagSet)(implicit ctx: Context) = { + Stats.record("Symbol.is") + ensureUpToDate() + (if (isCurrent(fs)) cachedFlags else flags).is(fs) + } + + /** Has this denotation one of the flags in `fs` set, whereas none of the flags + * in `butNot` are set? + */ + final def is(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context) = { + Stats.record("Symbol.is") + ensureUpToDate() + (if (isCurrent(fs) && isCurrent(butNot)) cachedFlags else flags).is(fs, butNot) + } + + /** Has this denotation all of the flags in `fs` set? */ + final def is(fs: FlagConjunction)(implicit ctx: Context) = { + Stats.record("Symbol.is") + ensureUpToDate() + (if (isCurrent(fs)) cachedFlags else flags).is(fs) + } + + /** Has this denotation all of the flags in `fs` set, whereas none of the flags + * in `butNot` are set? + */ + final def is(fs: FlagConjunction, butNot: FlagSet)(implicit ctx: Context) = { + Stats.record("Symbol.is") + ensureUpToDate() + (if (isCurrent(fs) && isCurrent(butNot)) cachedFlags else flags).is(fs, butNot) + } + + final def exists(implicit ctx: Context): Boolean = lastDenot.exists // updating necessary, any denot will do + + final def isRoot(implicit ctx: Context): Boolean = { + ensureUpToDate() + (maybeOwner eq NoSymbol) && (name.toTermName == nme.ROOT || name == nme.ROOTPKG) + } + + final def isEmptyPackage(implicit ctx: Context): Boolean = { + ensureUpToDate() + name.toTermName == nme.EMPTY_PACKAGE && owner.isRoot + } + + def isPrimitiveValueClass(implicit ctx: Context) = { + ensureUpToDate() + maybeOwner == defn.ScalaPackageClass && defn.ScalaValueClasses().contains(this) + } + + /** Is this symbol a package object or its module class? */ + def isPackageObject(implicit ctx: Context): Boolean = { + ensureUpToDate() + val nameMatches = + if (isType) name == tpnme.PACKAGE.moduleClassName + else name == nme.PACKAGE + nameMatches && (owner is Package) && (this is Module) + } + +// -------- Loading nested predefined symbols ----------------------------- + + def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol = + info.member(name.toTermName).requiredSymbol(_ is Method).asTerm + def requiredMethodRef(name: PreName)(implicit ctx: Context): TermRef = + requiredMethod(name).termRef + + def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = { + info.member(name.toTermName).requiredSymbol { x => + (x is Method) && { + x.info.paramInfoss match { + case paramInfos :: Nil => paramInfos.corresponds(argTypes)(_ =:= _) + case _ => false + } + } + }.asTerm + } + def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef = + requiredMethod(name, argTypes).termRef + + def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol = + info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm + def requiredValueRef(name: PreName)(implicit ctx: Context): TermRef = + requiredValue(name).termRef + + def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = + info.member(name.toTypeName).requiredSymbol(_.isClass).asClass + + def requiredType(name: PreName)(implicit ctx: Context): TypeSymbol = + info.member(name.toTypeName).requiredSymbol(_.isType).asType + // -------- Printing -------------------------------------------------------- /** The prefix string to be used when displaying this symbol without denotation */ From ed335ded13093d36c6b3fc968dd02a06b4b0d4bb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 12 May 2018 10:48:18 +0200 Subject: [PATCH 07/12] Throw an exception for NoSymbol.owner This preserves the former behavior on NoDenotation. --- compiler/src/dotty/tools/dotc/core/Symbols.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 4a521cdea6ea..1bdfac0749bc 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -891,6 +891,7 @@ object Symbols { } @sharable object NoSymbol extends Symbol(NoCoord, 0) { + override def owner(implicit ctx: Context): Symbol = throw new AssertionError("NoSymbol.owner") override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file override def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = NoDenotation } From 9cb1eed45ca6de94441c0370c838d619cc55327d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 12 May 2018 10:59:42 +0200 Subject: [PATCH 08/12] Also cache Denotation#validFor in Symbol --- .../dotty/tools/dotc/core/Denotations.scala | 2 +- .../src/dotty/tools/dotc/core/Symbols.scala | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 4ace9fed3549..7c6257fa8bb6 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -689,7 +689,7 @@ object Denotations { def validFor = myValidFor def validFor_=(p: Period) = { myValidFor = p - symbol.invalidateDenotCache() + symbol.updateValidForCache(this, p) } /** The next SingleDenotation in this run, with wrap-around from last to first. diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 1bdfac0749bc..34fde994d5cd 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -434,12 +434,16 @@ object Symbols { private[this] var lastDenot: SymDenotation = _ private[this] var checkedPeriod: Period = Nowhere + private[this] var cachedValidFor: Period = _ private[this] var cachedName: Name = _ private[this] var cachedOwner: Symbol = _ private[this] var cachedInfo: Type = _ private[this] var cachedFlags: FlagSet = _ - private[core] def invalidateDenotCache() = { checkedPeriod = Nowhere } + private[core] def updateValidForCache(d: Denotation, validFor: Period) = { + if (lastDenot `eq` d) cachedValidFor = validFor + checkedPeriod = Nowhere // this forces recomputeDenot() on next call to denot + } private[core] def updateInfoCache(d: SymDenotation, info: Type) = if (lastDenot `eq` d) cachedInfo = info @@ -449,6 +453,7 @@ object Symbols { private[this] def setLastDenot(d: SymDenotation) = { lastDenot = d + cachedValidFor = d.validFor cachedName = d.name cachedOwner = d.maybeOwner cachedInfo = d.infoOrCompleter @@ -464,22 +469,21 @@ object Symbols { /** The current denotation of this symbol */ final def denot(implicit ctx: Context): SymDenotation = { Stats.record("Symbol.denot") - val lastd = lastDenot - if (checkedPeriod == ctx.period) lastd - else computeDenot(lastd) + if (checkedPeriod == ctx.period) lastDenot + else computeDenot() } - private def computeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = { + private def computeDenot()(implicit ctx: Context): SymDenotation = { Stats.record("Symbol.computeDenot") val now = ctx.period checkedPeriod = now - if (lastd.validFor contains now) lastd else recomputeDenot(lastd) + if (cachedValidFor contains now) lastDenot else recomputeDenot() } /** Overridden in NoSymbol */ - protected def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context) = { + protected def recomputeDenot()(implicit ctx: Context) = { Stats.record("Symbol.recomputeDenot") - val newd = lastd.current.asInstanceOf[SymDenotation] + val newd = lastDenot.current.asInstanceOf[SymDenotation] setLastDenot(newd) newd } @@ -638,7 +642,7 @@ object Symbols { // -------- Cached SymDenotation facades ----------------------------------------------- private def ensureUpToDate()(implicit ctx: Context) = - if (checkedPeriod != ctx.period) computeDenot(lastDenot) + if (checkedPeriod != ctx.period) computeDenot() /** The current name of this symbol */ final def name(implicit ctx: Context): ThisName = { @@ -893,7 +897,7 @@ object Symbols { @sharable object NoSymbol extends Symbol(NoCoord, 0) { override def owner(implicit ctx: Context): Symbol = throw new AssertionError("NoSymbol.owner") override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file - override def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = NoDenotation + override def recomputeDenot()(implicit ctx: Context): SymDenotation = NoDenotation } NoDenotation // force it in order to set `denot` field of NoSymbol From 4b421b6995e0ca9e0fe0a08694b9016c43e369c2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 12 May 2018 13:08:14 +0200 Subject: [PATCH 09/12] Cache NamedTypes with thisType prefix in Symbols ... rather than the global ctx.uniqueNamedRefs hashtable. This reduces the size of the hashtable by a bit more than half --- .../tools/dotc/core/SymDenotations.scala | 20 ++++++++++++-- .../src/dotty/tools/dotc/core/Uniques.scala | 27 +++++++++++++++---- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 2f8b864aa35d..de9723a2272e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1175,11 +1175,27 @@ object SymDenotations { /** The type This(cls), where cls is this class, NoPrefix for all other symbols */ def thisType(implicit ctx: Context): Type = NoPrefix + private[this] var cachedRef: NamedType = null + + def namedRef(implicit ctx: Context): NamedType = { + if (cachedRef == null) { + val initl = initial + val thistpe = maybeOwner.thisType + if ((initl `ne` this) && (thistpe `eq` initl.maybeOwner.thisType)) + cachedRef = initial.namedRef + else { + Stats.record("namedRef create") + cachedRef = ctx.uniqueNamedTypes.newType(thistpe, symbol, isTerm) + } + } + cachedRef + } + override def typeRef(implicit ctx: Context): TypeRef = - TypeRef(owner.thisType, symbol) + namedRef.asInstanceOf[TypeRef] override def termRef(implicit ctx: Context): TermRef = - TermRef(owner.thisType, symbol) + namedRef.asInstanceOf[TermRef] /** The variance of this type parameter or type member as an Int, with * +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter diff --git a/compiler/src/dotty/tools/dotc/core/Uniques.scala b/compiler/src/dotty/tools/dotc/core/Uniques.scala index f31dc4f389b8..642ca5f4526a 100644 --- a/compiler/src/dotty/tools/dotc/core/Uniques.scala +++ b/compiler/src/dotty/tools/dotc/core/Uniques.scala @@ -53,16 +53,33 @@ object Uniques { e } + def newType(prefix: Type, designator: Designator, hc: Int, isTerm: Boolean): NamedType = + if (isTerm) new CachedTermRef(prefix, designator, hc) + else new CachedTypeRef(prefix, designator, hc) + + def newType(prefix: Type, designator: Designator, isTerm: Boolean)(implicit ctx: Context): NamedType = + newType(prefix, designator, doHash(null, designator, prefix), isTerm) + def enterIfNew(prefix: Type, designator: Designator, isTerm: Boolean)(implicit ctx: Context): NamedType = { val h = doHash(null, designator, prefix) if (monitored) recordCaching(h, classOf[NamedType]) - def newType = - if (isTerm) new CachedTermRef(prefix, designator, h) - else new CachedTypeRef(prefix, designator, h) - if (h == NotCached) newType + designator match { + case sym: Symbol => + val symd = sym.lastKnownDenotation + if (symd != null) { + val ref = symd.namedRef + if (prefix `eq` ref.prefix) { + record("namedRef reuse") + return ref + } + } + case _ => + } + if (h == NotCached) newType(prefix, designator, h, isTerm) else { val r = findPrevious(h, prefix, designator) - if ((r ne null) && (r.isTerm == isTerm)) r else addEntryAfterScan(newType) + if ((r ne null) && (r.isTerm == isTerm)) r + else addEntryAfterScan(newType(prefix, designator, h, isTerm)) } } } From 7eaa3d2a9d26c3f68cfb5b036b6cf6d553e4c7ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 12 May 2018 13:53:53 +0200 Subject: [PATCH 10/12] Harden new caching scheme against fuzzing --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 12 ++++++++---- compiler/src/dotty/tools/dotc/core/Uniques.scala | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index de9723a2272e..804c7cf3a384 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1191,11 +1191,15 @@ object SymDenotations { cachedRef } - override def typeRef(implicit ctx: Context): TypeRef = - namedRef.asInstanceOf[TypeRef] + override def typeRef(implicit ctx: Context): TypeRef = namedRef match { + case ref: TypeRef => ref + case _ => defn.AnyClass.typeRef // case arises when compiling parser-stabiility-1.scala + } - override def termRef(implicit ctx: Context): TermRef = - namedRef.asInstanceOf[TermRef] + override def termRef(implicit ctx: Context): TermRef = namedRef match { + case ref: TermRef => ref + case _ => defn.EmptyPackageVal.termRef + } /** The variance of this type parameter or type member as an Int, with * +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter diff --git a/compiler/src/dotty/tools/dotc/core/Uniques.scala b/compiler/src/dotty/tools/dotc/core/Uniques.scala index 642ca5f4526a..ab9913d52701 100644 --- a/compiler/src/dotty/tools/dotc/core/Uniques.scala +++ b/compiler/src/dotty/tools/dotc/core/Uniques.scala @@ -66,7 +66,7 @@ object Uniques { designator match { case sym: Symbol => val symd = sym.lastKnownDenotation - if (symd != null) { + if (symd != null && symd.exists) { val ref = symd.namedRef if (prefix `eq` ref.prefix) { record("namedRef reuse") From 301886744055ed3076393f38ba9da577c4e6bbde Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 12 May 2018 14:16:41 +0200 Subject: [PATCH 11/12] Revert: Also cache Denotation#validFor in Symbol Reverted from commit 7587f9c817729e84f3ba2677e8733bdf950f7893 This one seemed to cause a performance regression. --- .../dotty/tools/dotc/core/Denotations.scala | 2 +- .../src/dotty/tools/dotc/core/Symbols.scala | 24 ++++++++----------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 7c6257fa8bb6..4ace9fed3549 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -689,7 +689,7 @@ object Denotations { def validFor = myValidFor def validFor_=(p: Period) = { myValidFor = p - symbol.updateValidForCache(this, p) + symbol.invalidateDenotCache() } /** The next SingleDenotation in this run, with wrap-around from last to first. diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 34fde994d5cd..1bdfac0749bc 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -434,16 +434,12 @@ object Symbols { private[this] var lastDenot: SymDenotation = _ private[this] var checkedPeriod: Period = Nowhere - private[this] var cachedValidFor: Period = _ private[this] var cachedName: Name = _ private[this] var cachedOwner: Symbol = _ private[this] var cachedInfo: Type = _ private[this] var cachedFlags: FlagSet = _ - private[core] def updateValidForCache(d: Denotation, validFor: Period) = { - if (lastDenot `eq` d) cachedValidFor = validFor - checkedPeriod = Nowhere // this forces recomputeDenot() on next call to denot - } + private[core] def invalidateDenotCache() = { checkedPeriod = Nowhere } private[core] def updateInfoCache(d: SymDenotation, info: Type) = if (lastDenot `eq` d) cachedInfo = info @@ -453,7 +449,6 @@ object Symbols { private[this] def setLastDenot(d: SymDenotation) = { lastDenot = d - cachedValidFor = d.validFor cachedName = d.name cachedOwner = d.maybeOwner cachedInfo = d.infoOrCompleter @@ -469,21 +464,22 @@ object Symbols { /** The current denotation of this symbol */ final def denot(implicit ctx: Context): SymDenotation = { Stats.record("Symbol.denot") - if (checkedPeriod == ctx.period) lastDenot - else computeDenot() + val lastd = lastDenot + if (checkedPeriod == ctx.period) lastd + else computeDenot(lastd) } - private def computeDenot()(implicit ctx: Context): SymDenotation = { + private def computeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = { Stats.record("Symbol.computeDenot") val now = ctx.period checkedPeriod = now - if (cachedValidFor contains now) lastDenot else recomputeDenot() + if (lastd.validFor contains now) lastd else recomputeDenot(lastd) } /** Overridden in NoSymbol */ - protected def recomputeDenot()(implicit ctx: Context) = { + protected def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context) = { Stats.record("Symbol.recomputeDenot") - val newd = lastDenot.current.asInstanceOf[SymDenotation] + val newd = lastd.current.asInstanceOf[SymDenotation] setLastDenot(newd) newd } @@ -642,7 +638,7 @@ object Symbols { // -------- Cached SymDenotation facades ----------------------------------------------- private def ensureUpToDate()(implicit ctx: Context) = - if (checkedPeriod != ctx.period) computeDenot() + if (checkedPeriod != ctx.period) computeDenot(lastDenot) /** The current name of this symbol */ final def name(implicit ctx: Context): ThisName = { @@ -897,7 +893,7 @@ object Symbols { @sharable object NoSymbol extends Symbol(NoCoord, 0) { override def owner(implicit ctx: Context): Symbol = throw new AssertionError("NoSymbol.owner") override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file - override def recomputeDenot()(implicit ctx: Context): SymDenotation = NoDenotation + override def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = NoDenotation } NoDenotation // force it in order to set `denot` field of NoSymbol From 987f4f8df52d0c5b9aff12c79249f36ef9fd0af1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 7 Jun 2018 17:51:07 +0200 Subject: [PATCH 12/12] Fix rebase breakage --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b436a12b98c6..17d4c8b5a7b1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -664,10 +664,9 @@ class Definitions { def Unpickler_unpickleType = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType") lazy val TastyUniverseModule = ctx.requiredModule("scala.tasty.Universe") - def TastyUniverseModuleClass(implicit ctx: Context) = TastyUniverseModule.symbol.asClass + def TastyUniverseModuleClass(implicit ctx: Context) = TastyUniverseModule.moduleClass.asClass - lazy val TastyUniverse_compilationUniverseR = TastyUniverseModule.requiredMethod("compilationUniverse") - def TastyUniverse_compilationUniverse(implicit ctx: Context) = TastyUniverse_compilationUniverseR.symbol + lazy val TastyUniverse_compilationUniverse = TastyUniverseModule.requiredMethod("compilationUniverse") lazy val EqType = ctx.requiredClassRef("scala.Eq") def EqClass(implicit ctx: Context) = EqType.symbol.asClass