diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index f19708c4ec97..13dd45037813 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -4,7 +4,6 @@ object Config { final val cacheMembersNamed = true final val cacheAsSeenFrom = true - final val useFingerPrints = true // note: it currently seems to be slightly faster not to use them! my junit test: 548s without, 560s with. final val cacheMemberNames = true final val cacheImplicitScopes = true diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index b299de4340c3..ff11ec35cf54 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -597,26 +597,6 @@ object Contexts { def nextId = { _nextId += 1; _nextId } - /** A map from a superclass id to the typeref of the class that has it */ - private[core] var classOfId = new Array[ClassSymbol](Config.InitialSuperIdsSize) - - /** A map from a the typeref of a class to its superclass id */ - private[core] val superIdOfClass = new mutable.AnyRefMap[ClassSymbol, Int] - - /** The last allocated superclass id */ - private[core] var lastSuperId = -1 - - /** Allocate and return next free superclass id */ - private[core] def nextSuperId: Int = { - lastSuperId += 1 - if (lastSuperId >= classOfId.length) { - val tmp = new Array[ClassSymbol](classOfId.length * 2) - classOfId.copyToArray(tmp) - classOfId = tmp - } - lastSuperId - } - // Types state /** A table for hash consing unique types */ private[core] val uniques = new util.HashSet[Type](Config.initialUniquesCapacity) { @@ -682,9 +662,6 @@ object Contexts { def reset() = { for ((_, set) <- uniqueSets) set.clear() - for (i <- 0 until classOfId.length) classOfId(i) = null - superIdOfClass.clear() - lastSuperId = -1 } // Test that access is single threaded diff --git a/compiler/src/dotty/tools/dotc/core/DenotTransformers.scala b/compiler/src/dotty/tools/dotc/core/DenotTransformers.scala index 02d27ea3389e..c62008e6eada 100644 --- a/compiler/src/dotty/tools/dotc/core/DenotTransformers.scala +++ b/compiler/src/dotty/tools/dotc/core/DenotTransformers.scala @@ -44,8 +44,10 @@ object DenotTransformers { val info1 = transformInfo(ref.info, ref.symbol) if (info1 eq ref.info) ref else ref match { - case ref: SymDenotation => ref.copySymDenotation(info = info1) - case _ => ref.derivedSingleDenotation(ref.symbol, info1) + case ref: SymDenotation => + ref.copySymDenotation(info = info1).copyCaches(ref, ctx.phase.next) + case _ => + ref.derivedSingleDenotation(ref.symbol, info1) } } } diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index c0bad8ded708..64c0bedddc6b 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -805,7 +805,7 @@ object Denotations { val transformer = ctx.denotTransformers(nextTransformerId) //println(s"transforming $this with $transformer") try { - next = transformer.transform(cur)(ctx.withPhase(transformer)).syncWithParents + next = transformer.transform(cur)(ctx.withPhase(transformer)) } catch { case ex: CyclicReference => println(s"error while transforming $this") // DEBUG @@ -817,7 +817,6 @@ object Denotations { next match { case next: ClassDenotation => assert(!next.is(Package), s"illegal transformation of package denotation by transformer ${ctx.withPhase(transformer).phase}") - next.resetFlag(Frozen) case _ => } next.insertAfter(cur) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index c1929d882b21..61b057f85b08 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -349,9 +349,6 @@ object Flags { /** A bridge method. Set by Erasure */ final val Bridge = termFlag(34, "") - /** All class attributes are fully defined */ - final val FullyCompleted = typeFlag(34, "") - /** Symbol is a Java varargs bridge */ // (needed?) final val VBridge = termFlag(35, "") // TODO remove @@ -375,9 +372,6 @@ object Flags { /** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */ final val Touched = commonFlag(48, "") - /** Class is not allowed to accept new members because fingerprint of subclass has been taken */ - final val Frozen = commonFlag(49, "") - /** An error symbol */ final val Erroneous = commonFlag(50, "") @@ -450,7 +444,7 @@ object Flags { Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessorOrBaseTypeArg | - Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | + Fresh | Erroneous | ImplicitCommon | Permanent | Synthetic | SuperAccessorOrScala2x | Inline /** Flags guaranteed to be set upon symbol creation, or, if symbol is a top-level @@ -558,6 +552,9 @@ object Flags { /** A lazy or deferred value */ final val LazyOrDeferred = Lazy | Deferred + /** An accessor or label */ + final val AccessorOrLabel = Accessor | Label + /** A synthetic or private definition */ final val SyntheticOrPrivate = Synthetic | Private diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 44608296a2b1..bf33471cc98f 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -305,6 +305,12 @@ object Phases { */ def isTyper = false + /** Can this transform create or delete non-private members? */ + def changesMembers: Boolean = false + + /** Can this transform change the parents of a class? */ + def changesParents: Boolean = false + def exists: Boolean = true private var myPeriod: Period = Periods.InvalidPeriod @@ -315,6 +321,8 @@ object Phases { private var mySymbolicRefs = false private var myLabelsReordered = false + private var mySameMembersStartId = NoPhaseId + private var mySameParentsStartId = NoPhaseId /** The sequence position of this phase in the given context where 0 * is reserved for NoPhase and the first real phase is at position 1. @@ -332,6 +340,11 @@ object Phases { final def symbolicRefs = mySymbolicRefs // Phase is after ResolveSuper, newly generated TermRefs should be symbolic final def labelsReordered = myLabelsReordered // Phase is after LabelDefs, labels are flattened and owner chains don't mirror this + final def sameMembersStartId = mySameMembersStartId + // id of first phase where all symbols are guaranteed to have the same members as in this phase + final def sameParentsStartId = mySameParentsStartId + // id of first phase where all symbols are guaranteed to have the same parents as in this phase + protected[Phases] def init(base: ContextBase, start: Int, end:Int): Unit = { if (start >= FirstPhaseId) assert(myPeriod == Periods.InvalidPeriod, s"phase $this has already been used once; cannot be reused") @@ -342,6 +355,8 @@ object Phases { myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked mySymbolicRefs = prev.getClass == classOf[ResolveSuper] || prev.symbolicRefs myLabelsReordered = prev.getClass == classOf[LabelDefs] || prev.labelsReordered + mySameMembersStartId = if (changesMembers) id else prev.sameMembersStartId + mySameParentsStartId = if (changesParents) id else prev.sameMembersStartId } protected[Phases] def init(base: ContextBase, id: Int): Unit = init(base, id, id) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index d3865d1e0ce1..8b2c34804e85 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -4,7 +4,7 @@ package core import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._ import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._, Comments._ -import NameOps._, NameKinds._ +import NameOps._, NameKinds._, Phases._ import Scopes.Scope import collection.mutable import collection.BitSet @@ -15,6 +15,7 @@ import annotation.tailrec import CheckRealizable._ import util.SimpleMap import util.Stats +import java.util.WeakHashMap import config.Config import config.Printers.{completions, incremental, noPrinter} @@ -1167,8 +1168,10 @@ object SymDenotations { privateWithin: Symbol = null, annotations: List[Annotation] = null)(implicit ctx: Context) = { // simulate default parameters, while also passing implicit context ctx to the default values - val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) &~ Frozen + val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) val info1 = if (info != null) info else this.info + if (ctx.isAfterTyper && changedClassParents(info, info1, completersMatter = false)) + assert(ctx.phase.changesParents, i"undeclared parent change at ${ctx.phase} for $this, was: $info, now: $info1") val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin val annotations1 = if (annotations != null) annotations else this.annotations val d = ctx.SymDenotation(symbol, owner, name, initFlags1, info1, privateWithin1) @@ -1176,6 +1179,24 @@ object SymDenotations { d } + /** Copy mamberNames and baseData caches from given denotation, provided + * they are valid at given `phase`. + */ + def copyCaches(from: SymDenotation, phase: Phase)(implicit ctx: Context): this.type = this + + /** Are `info1` and `info2` ClassInfo types with different parents? + * @param completersMatter if `true`, consider parents changed if `info1` or `info2 `is a type completer + */ + protected def changedClassParents(info1: Type, info2: Type, completersMatter: Boolean): Boolean = + info2 match { + case info2: ClassInfo => + info1 match { + case info1: ClassInfo => info1.classParents ne info2.classParents + case _ => completersMatter + } + case _ => completersMatter + } + override def initial: SymDenotation = super.initial.asSymDenotation /** Install this denotation as the result of the given denotation transformer. */ @@ -1209,6 +1230,68 @@ object SymDenotations { import util.LRUCache + // ----- caches ------------------------------------------------------- + + private[this] var myTypeParams: List[TypeSymbol] = null + private[this] var fullNameCache: SimpleMap[QualifiedNameKind, Name] = SimpleMap.Empty + + private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null + private[this] var myMemberCachePeriod: Period = Nowhere + + /** A cache from types T to baseTypeRef(T, C) */ + type BaseTypeRefMap = java.util.HashMap[CachedType, Type] + private[this] var myBaseTypeRefCache: BaseTypeRefMap = null + private[this] var myBaseTypeRefCachePeriod: Period = Nowhere + + private var baseDataCache: BaseData = BaseData.None + private var memberNamesCache: MemberNames = MemberNames.None + + private def memberCache(implicit ctx: Context): LRUCache[Name, PreDenotation] = { + if (myMemberCachePeriod != ctx.period) { + myMemberCache = new LRUCache + myMemberCachePeriod = ctx.period + } + myMemberCache + } + + private def baseTypeRefCache(implicit ctx: Context): BaseTypeRefMap = { + if (myBaseTypeRefCachePeriod != ctx.period && + (myBaseTypeRefCachePeriod.runId != ctx.runId || + ctx.phases(myBaseTypeRefCachePeriod.phaseId).sameParentsStartId != ctx.phase.sameParentsStartId)) { + myBaseTypeRefCache = new BaseTypeRefMap + myBaseTypeRefCachePeriod = ctx.period + } + myBaseTypeRefCache + } + + private def invalidateBaseDataCache() = { + baseDataCache.invalidate() + baseDataCache = BaseData.None + } + + private def invalidateMemberNamesCache() = { + memberNamesCache.invalidate() + memberNamesCache = MemberNames.None + } + + def invalidateBaseTypeRefCache() = { + myBaseTypeRefCache = null + myBaseTypeRefCachePeriod = Nowhere + } + + override def copyCaches(from: SymDenotation, phase: Phase)(implicit ctx: Context): this.type = { + from match { + case from: ClassDenotation => + if (from.memberNamesCache.isValidAt(phase)) memberNamesCache = from.memberNamesCache + if (from.baseDataCache.isValidAt(phase)) { + baseDataCache = from.baseDataCache + myBaseTypeRefCache = from.baseTypeRefCache + } + case _ => + } + this + } + // ----- denotation fields and accessors ------------------------------ if (initFlags is (Module, butNot = Package)) @@ -1220,9 +1303,6 @@ object SymDenotations { /** The info asserted to have type ClassInfo */ def classInfo(implicit ctx: Context): ClassInfo = info.asInstanceOf[ClassInfo] - /** TODO: Document why caches are supposedly safe to use */ - private[this] var myTypeParams: List[TypeSymbol] = _ - /** The type parameters in this class, in the order they appear in the current * scope `decls`. This might be temporarily the incorrect order when * reading Scala2 pickled info. The problem is fixed by `ensureTypeParamsInCorrectOrder`, @@ -1249,8 +1329,11 @@ object SymDenotations { } override protected[dotc] final def info_=(tp: Type) = { - super.info_=(tp) + if (changedClassParents(infoOrCompleter, tp, completersMatter = true)) + invalidateBaseDataCache() + invalidateMemberNamesCache() myTypeParams = null // changing the info might change decls, and with it typeParams + super.info_=(tp) } /** The denotations of all parents in this class. */ @@ -1268,63 +1351,6 @@ object SymDenotations { NoSymbol } - /** The denotation is fully completed: all attributes are fully defined. - * ClassDenotations compiled from source are first completed, then fully completed. - * Packages are never fully completed since members can be added at any time. - * @see Namer#ClassCompleter - */ - private def isFullyCompleted(implicit ctx: Context): Boolean = { - def isFullyCompletedRef(tp: TypeRef) = tp.denot match { - case d: ClassDenotation => d.isFullyCompleted - case _ => false - } - def testFullyCompleted = - if (classParents.isEmpty) !is(Package) && symbol.eq(defn.AnyClass) - else classParents.forall(isFullyCompletedRef) - flagsUNSAFE.is(FullyCompleted) || - isCompleted && testFullyCompleted && { setFlag(FullyCompleted); true } - } - - // ------ syncing inheritance-related info ----------------------------- - - private var firstRunId: RunId = initRunId - - /** invalidate caches influenced by parent classes if one of the parents - * is younger than the denotation itself. - */ - override def syncWithParents(implicit ctx: Context): SingleDenotation = { - def isYounger(tref: TypeRef) = tref.symbol.denot match { - case denot: ClassDenotation => - if (denot.validFor.runId < ctx.runId) denot.current // syncs with its parents in turn - val result = denot.firstRunId > this.firstRunId - if (result) incremental.println(s"$denot is younger than $this") - result - case _ => false - } - val parentIsYounger = (firstRunId < ctx.runId) && { - infoOrCompleter match { - case cinfo: ClassInfo => cinfo.classParents exists isYounger - case _ => false - } - } - if (parentIsYounger) { - incremental.println(s"parents of $this are invalid; symbol id = ${symbol.id}, copying ...\n") - invalidateInheritedInfo() - } - firstRunId = ctx.runId - this - } - - /** Invalidate all caches and fields that depend on base classes and their contents */ - override def invalidateInheritedInfo(): Unit = { - myBaseClasses = null - mySuperClassBits = null - myMemberFingerPrint = FingerPrint.unknown - myMemberCache = null - myMemberCachePeriod = Nowhere - memberNamesCache = SimpleMap.Empty - } - // ------ class-specific operations ----------------------------------- private[this] var myThisType: Type = null @@ -1357,78 +1383,55 @@ object SymDenotations { myTypeRef } - private[this] var myBaseClasses: List[ClassSymbol] = null - private[this] var mySuperClassBits: BitSet = null + private def baseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = { + if (!baseDataCache.isValid) baseDataCache = BaseData.newCache() + baseDataCache(this) + } - /** Invalidate baseTypeRefCache, baseClasses and superClassBits on new run */ - private def checkBasesUpToDate()(implicit ctx: Context) = - if (baseTypeRefValid != ctx.runId) { - invalidateBaseTypeRefCache() - myBaseClasses = null - mySuperClassBits = null - baseTypeRefValid = ctx.runId - } + /** The base classes of this class in linearization order, + * with the class itself as first element. + */ + def baseClasses(implicit onBehalf: BaseData, ctx: Context): List[ClassSymbol] = + baseData._1 - def invalidateBaseTypeRefCache() = - baseTypeRefCache = new java.util.HashMap[CachedType, Type] + /** A bitset that contains the superId's of all base classes */ + private def baseClassSet(implicit onBehalf: BaseData, ctx: Context): BaseClassSet = + baseData._2 - private def computeBases(implicit ctx: Context): (List[ClassSymbol], BitSet) = { - if (myBaseClasses eq Nil) throw CyclicReference(this) - myBaseClasses = Nil - val seen = new mutable.BitSet - val locked = new mutable.BitSet + def computeBaseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = { + val seen = mutable.SortedSet[Int]() def addBaseClasses(bcs: List[ClassSymbol], to: List[ClassSymbol]) : List[ClassSymbol] = bcs match { case bc :: bcs1 => val bcs1added = addBaseClasses(bcs1, to) - val id = bc.superId - if (seen contains id) bcs1added + if (seen contains bc.id) bcs1added else { - seen += id + seen += bc.id bc :: bcs1added } case nil => to } - def addParentBaseClasses(ps: List[Type], to: List[ClassSymbol]): List[ClassSymbol] = ps match { + def addParentBaseClasses(ps: List[TypeRef], to: List[ClassSymbol]): List[ClassSymbol] = ps match { case p :: ps1 => - addParentBaseClasses(ps1, addBaseClasses(p.baseClasses, to)) + addParentBaseClasses(ps1, + addBaseClasses(p.symbol.asClass.baseClasses, to)) case nil => to } - val bcs = classSymbol :: addParentBaseClasses(classParents, Nil) - val scbits = seen - if (isFullyCompleted) { - myBaseClasses = bcs - mySuperClassBits = scbits - } - else myBaseClasses = null - (bcs, scbits) + def emptyParentsExpected = + is(Package) || (symbol == defn.AnyClass) || ctx.erasedTypes && (symbol == defn.ObjectClass) + if (classParents.isEmpty && !emptyParentsExpected) + onBehalf.signalProvisional() + (classSymbol :: addParentBaseClasses(classParents, Nil), + new BaseClassSet(seen.toArray)) } - /** A bitset that contains the superId's of all base classes */ - private def superClassBits(implicit ctx: Context): BitSet = - if (classParents.isEmpty) BitSet() // can happen when called too early in Namers - else { - checkBasesUpToDate() - if (mySuperClassBits != null) mySuperClassBits else computeBases._2 - } - - /** The base classes of this class in linearization order, - * with the class itself as first element. - */ - def baseClasses(implicit ctx: Context): List[ClassSymbol] = - if (classParents.isEmpty) classSymbol :: Nil // can happen when called too early in Namers - else { - checkBasesUpToDate() - if (myBaseClasses != null) myBaseClasses else computeBases._1 - } - final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = !isAbsent && base.isClass && ( (symbol eq base) - || (superClassBits contains base.superId) + || (baseClassSet contains base) || (this is Erroneous) || (base is Erroneous) ) @@ -1441,53 +1444,6 @@ object SymDenotations { final override def typeParamCreationFlags = ClassTypeParamCreationFlags - private[this] var myMemberFingerPrint: FingerPrint = FingerPrint.unknown - - private def computeMemberFingerPrint(implicit ctx: Context): FingerPrint = { - var fp = FingerPrint() - var e = info.decls.lastEntry - while (e != null) { - fp.include(e.name) - e = e.prev - } - var ps = classParents - while (ps.nonEmpty) { - val parent = ps.head.typeSymbol - parent.denot match { - case parentDenot: ClassDenotation => - fp.include(parentDenot.memberFingerPrint) - if (parentDenot.isFullyCompleted) parentDenot.setFlag(Frozen) - case _ => - } - ps = ps.tail - } - fp - } - - /** A bloom filter for the names of all members in this class. - * Makes sense only for parent classes, and should definitely - * not be used for package classes because cache never - * gets invalidated. - */ - def memberFingerPrint(implicit ctx: Context): FingerPrint = - if (myMemberFingerPrint != FingerPrint.unknown) myMemberFingerPrint - else { - val fp = computeMemberFingerPrint - if (isFullyCompleted) myMemberFingerPrint = fp - fp - } - - private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null - private[this] var myMemberCachePeriod: Period = Nowhere - - private def memberCache(implicit ctx: Context): LRUCache[Name, PreDenotation] = { - if (myMemberCachePeriod != ctx.period) { - myMemberCache = new LRUCache - myMemberCachePeriod = ctx.period - } - myMemberCache - } - /** Hook to do a pre-enter test. Overridden in PackageDenotation */ protected def proceedWithEnter(sym: Symbol, mscope: MutableScope)(implicit ctx: Context): Boolean = true @@ -1519,21 +1475,9 @@ object SymDenotations { /** Enter a symbol in given `scope` without potentially replacing the old copy. */ def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = { - def isUsecase = ctx.docCtx.isDefined && sym.name.show.takeRight(4) == "$doc" - require( - (sym.denot.flagsUNSAFE is Private) || - !(this is Frozen) || - (scope ne this.unforcedDecls) || - sym.hasAnnotation(defn.ScalaStaticAnnot) || - sym.name.is(InlineAccessorName) || - isUsecase, i"trying to enter $sym in $this, frozen = ${this is Frozen}") - scope.enter(sym) - - if (myMemberFingerPrint != FingerPrint.unknown) - myMemberFingerPrint.include(sym.name) - if (myMemberCache != null) - myMemberCache invalidate sym.name + if (myMemberCache != null) myMemberCache.invalidate(sym.name) + if (!sym.flagsUNSAFE.is(Private)) invalidateMemberNamesCache() } /** Replace symbol `prev` (if defined in current class) by symbol `replacement`. @@ -1541,10 +1485,8 @@ object SymDenotations { * @pre `prev` and `replacement` have the same name. */ def replace(prev: Symbol, replacement: Symbol)(implicit ctx: Context): Unit = { - require(!(this is Frozen)) unforcedDecls.openForMutations.replace(prev, replacement) - if (myMemberCache != null) - myMemberCache invalidate replacement.name + if (myMemberCache != null) myMemberCache.invalidate(replacement.name) } /** Delete symbol from current scope. @@ -1552,10 +1494,9 @@ object SymDenotations { * someone does a findMember on a subclass. */ def delete(sym: Symbol)(implicit ctx: Context) = { - require(!(this is Frozen)) info.decls.openForMutations.unlink(sym) - myMemberFingerPrint = FingerPrint.unknown - if (myMemberCache != null) myMemberCache invalidate sym.name + if (myMemberCache != null) myMemberCache.invalidate(sym.name) + if (!sym.flagsUNSAFE.is(Private)) invalidateMemberNamesCache() } /** Make sure the type parameters of this class appear in the order given @@ -1593,7 +1534,7 @@ object SymDenotations { var denots: PreDenotation = memberCache lookup name if (denots == null) { denots = computeNPMembersNamed(name, inherited) - if (isFullyCompleted) memberCache.enter(name, denots) + memberCache.enter(name, denots) } else if (Config.checkCacheMembersNamed) { val denots1 = computeNPMembersNamed(name, inherited) assert(denots.exists == denots1.exists, s"cache inconsistency: cached: $denots, computed $denots1, name = $name, owner = $this") @@ -1603,31 +1544,27 @@ object SymDenotations { } private[core] def computeNPMembersNamed(name: Name, inherited: Boolean)(implicit ctx: Context): PreDenotation = /*>|>*/ Stats.track("computeNPMembersNamed") /*<|<*/ { - if (!inherited || - !Config.useFingerPrints || - (memberFingerPrint contains name)) { - Stats.record("computeNPMembersNamed after fingerprint") - ensureCompleted() - val ownDenots = info.decls.denotsNamed(name, selectNonPrivate) - if (debugTrace) // DEBUG - println(s"$this.member($name), ownDenots = $ownDenots") - def collect(denots: PreDenotation, parents: List[TypeRef]): PreDenotation = parents match { - case p :: ps => - val denots1 = collect(denots, ps) - p.symbol.denot match { - case parentd: ClassDenotation => - denots1 union - parentd.nonPrivateMembersNamed(name, inherited = true) - .mapInherited(ownDenots, denots1, thisType) - case _ => - denots1 - } - case nil => - denots - } - if (name.isConstructorName) ownDenots - else collect(ownDenots, classParents) - } else NoDenotation + Stats.record("computeNPMembersNamed after fingerprint") + ensureCompleted() + val ownDenots = info.decls.denotsNamed(name, selectNonPrivate) + if (debugTrace) // DEBUG + println(s"$this.member($name), ownDenots = $ownDenots") + def collect(denots: PreDenotation, parents: List[TypeRef]): PreDenotation = parents match { + case p :: ps => + val denots1 = collect(denots, ps) + p.symbol.denot match { + case parentd: ClassDenotation => + denots1 union + parentd.nonPrivateMembersNamed(name, inherited = true) + .mapInherited(ownDenots, denots1, thisType) + case _ => + denots1 + } + case nil => + denots + } + if (name.isConstructorName) ownDenots + else collect(ownDenots, classParents) } override final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = { @@ -1635,9 +1572,6 @@ object SymDenotations { raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre) } - private[this] var baseTypeRefCache: java.util.HashMap[CachedType, Type] = null - private[this] var baseTypeRefValid: RunId = NoRunId - /** Compute tp.baseTypeRef(this) */ final def baseTypeRefOf(tp: Type)(implicit ctx: Context): Type = { @@ -1646,8 +1580,6 @@ object SymDenotations { case _ => bt } - def inCache(tp: Type) = baseTypeRefCache.containsKey(tp) - /** We cannot cache: * - type variables which are uninstantiated or whose instances can * change, depending on typerstate. @@ -1656,13 +1588,16 @@ object SymDenotations { * and this changes subtyping relations. As a shortcut, we do not * cache ErasedValueType at all. */ - def isCachable(tp: Type): Boolean = tp match { - case _: TypeErasure.ErasedValueType => false - case tp: TypeRef if tp.symbol.isClass => true - case tp: TypeVar => tp.inst.exists && inCache(tp.inst) - case tp: TypeProxy => inCache(tp.underlying) - case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) - case _ => true + def isCachable(tp: Type, btrCache: BaseTypeRefMap): Boolean = { + def inCache(tp: Type) = btrCache.containsKey(tp) + tp match { + case _: TypeErasure.ErasedValueType => false + case tp: TypeRef if tp.symbol.isClass => true + case tp: TypeVar => tp.inst.exists && inCache(tp.inst) + case tp: TypeProxy => inCache(tp.underlying) + case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) + case _ => true + } } def computeBaseTypeRefOf(tp: Type): Type = { @@ -1676,7 +1611,7 @@ object SymDenotations { tp else subcls.denot match { case cdenot: ClassDenotation => - if (cdenot.superClassBits contains symbol.superId) foldGlb(NoType, tp.parents) + if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parents) else NoType case _ => baseTypeRefOf(tp.superType) @@ -1697,21 +1632,21 @@ object SymDenotations { /*>|>*/ ctx.debugTraceIndented(s"$tp.baseTypeRef($this)") /*<|<*/ { tp match { case tp: CachedType => + val btrCache = baseTypeRefCache try { - checkBasesUpToDate() - var basetp = baseTypeRefCache get tp + var basetp = btrCache get tp if (basetp == null) { - baseTypeRefCache.put(tp, NoPrefix) + btrCache.put(tp, NoPrefix) basetp = computeBaseTypeRefOf(tp) - if (isCachable(tp)) baseTypeRefCache.put(tp, basetp) - else baseTypeRefCache.remove(tp) + if (isCachable(tp, baseTypeRefCache)) btrCache.put(tp, basetp) + else btrCache.remove(tp) } else if (basetp == NoPrefix) throw CyclicReference(this) basetp } catch { case ex: Throwable => - baseTypeRefCache.put(tp, null) + btrCache.put(tp, null) throw ex } case _ => @@ -1720,39 +1655,29 @@ object SymDenotations { } } - private[this] var memberNamesCache: SimpleMap[NameFilter, Set[Name]] = SimpleMap.Empty - - def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = { - def computeMemberNames: Set[Name] = { - var names = Set[Name]() - def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name - for (p <- classParents) - for (name <- p.memberNames(keepOnly, thisType)) maybeAdd(name) - val ownSyms = - if (keepOnly == implicitFilter) - if (this is Package) Iterator.empty - else info.decls.iterator filter (_ is Implicit) - else info.decls.iterator - for (sym <- ownSyms) maybeAdd(sym.name) - names - } - if ((this is PackageClass) || !Config.cacheMemberNames) - computeMemberNames // don't cache package member names; they might change + def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = + if ((this is PackageClass) || !Config.cacheMemberNames) + computeMemberNames(keepOnly) // don't cache package member names; they might change else { - val cached = memberNamesCache(keepOnly) - if (cached != null) cached - else { - val names = computeMemberNames - if (isFullyCompleted) { - setFlag(Frozen) - memberNamesCache = memberNamesCache.updated(keepOnly, names) - } - names - } + if (!memberNamesCache.isValid) memberNamesCache = MemberNames.newCache() + memberNamesCache(keepOnly, this) } + + def computeMemberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = { + var names = Set[Name]() + def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name + for (p <- classParents) + for (name <- p.symbol.asClass.memberNames(keepOnly)) + maybeAdd(name) + val ownSyms = + if (keepOnly eq implicitFilter) + if (this is Package) Iterator.empty + else info.decls.iterator filter (_ is Implicit) + else info.decls.iterator + for (sym <- ownSyms) maybeAdd(sym.name) + names } - private[this] var fullNameCache: SimpleMap[QualifiedNameKind, Name] = SimpleMap.Empty override final def fullNameSeparated(kind: QualifiedNameKind)(implicit ctx: Context): Name = { val cached = fullNameCache(kind) if (cached != null) cached @@ -1790,6 +1715,7 @@ object SymDenotations { val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo if (classInfo(prevCtx).decls eq decls) copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo)) + .copyCaches(this, phase.next) .installAfter(phase) } } @@ -1849,7 +1775,7 @@ object SymDenotations { } /** The union of the member names of the package and the package object */ - override def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = { + override def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = { val ownNames = super.memberNames(keepOnly) packageObj.moduleClass.denot match { case pcls: ClassDenotation => ownNames union pcls.memberNames(keepOnly) @@ -1992,41 +1918,173 @@ object SymDenotations { } } - // ---- Fingerprints ----------------------------------------------------- + // ---- Caches for inherited info ----------------------------------------- + + /** Base trait for caches that keep info dependent on inherited classes */ + trait InheritedCache { + + /** Is the cache valid in current period? */ + def isValid(implicit ctx: Context): Boolean + + /** is the cache valid in current run at given phase? */ + def isValidAt(phase: Phase)(implicit ctx: Context): Boolean + + /** Render invalid this cache and all cache that depend on it */ + def invalidate(): Unit + } + + /** A cache for sets of member names, indexed by a NameFilter */ + trait MemberNames extends InheritedCache { + def apply(keepOnly: NameFilter, clsd: ClassDenotation) + (implicit onBehalf: MemberNames, ctx: Context): Set[Name] + } - /** A fingerprint is a bitset that acts as a bloom filter for sets - * of names. + object MemberNames { + implicit val None: MemberNames = new InvalidCache with MemberNames { + def apply(keepOnly: NameFilter, clsd: ClassDenotation)(implicit onBehalf: MemberNames, ctx: Context) = ??? + } + def newCache()(implicit ctx: Context): MemberNames = new MemberNamesImpl(ctx.period) + } + + /** A cache for baseclasses, as a sequence in linearization order and as a set that + * can be queried efficiently for containment. */ - class FingerPrint(val bits: Array[Long]) extends AnyVal { - import FingerPrint._ + trait BaseData extends InheritedCache { + def apply(clsd: ClassDenotation) + (implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) + def signalProvisional(): Unit + } + + object BaseData { + implicit val None: BaseData = new InvalidCache with BaseData { + def apply(clsd: ClassDenotation)(implicit onBehalf: BaseData, ctx: Context) = ??? + def signalProvisional() = () + } + def newCache()(implicit ctx: Context): BaseData = new BaseDataImpl(ctx.period) + } + + private abstract class InheritedCacheImpl(val createdAt: Period) extends InheritedCache { + protected def sameGroup(p1: Phase, p2: Phase): Boolean + + private[this] var dependent: WeakHashMap[InheritedCache, Unit] = null + + protected def invalidateDependents() = { + if (dependent != null) { + val it = dependent.keySet.iterator() + while (it.hasNext()) it.next().invalidate() + } + dependent = null + } - /** Include some bits of name's hashcode in set */ - def include(name: Name): Unit = { - val hash = name.hashCode & Mask - bits(hash >> WordSizeLog) |= (1L << hash) + protected def addDependent(dep: InheritedCache) = { + if (dependent == null) dependent = new WeakHashMap + dependent.put(dep, ()) } - /** Include all bits of `that` fingerprint in set */ - def include(that: FingerPrint): Unit = - for (i <- 0 until NumWords) bits(i) |= that.bits(i) + def isValidAt(phase: Phase)(implicit ctx: Context) = + createdAt.runId == ctx.runId && sameGroup(ctx.phases(createdAt.phaseId), phase) + } + + private class InvalidCache extends InheritedCache { + def isValid(implicit ctx: Context) = false + def isValidAt(phase: Phase)(implicit ctx: Context) = false + def invalidate(): Unit = () + } + + private class MemberNamesImpl(createdAt: Period) extends InheritedCacheImpl(createdAt) with MemberNames { + private[this] var cache: SimpleMap[NameFilter, Set[Name]] = SimpleMap.Empty + + final def isValid(implicit ctx: Context): Boolean = + cache != null && isValidAt(ctx.phase) + + private var locked = false + + /** Computing parent member names might force parents, which could invalidate + * the cache itself. In that case we should cancel invalidation and + * proceed as usual. However, all cache entries should be cleared. + */ + def invalidate(): Unit = + if (cache != null) + if (locked) cache = SimpleMap.Empty + else { + cache = null + invalidateDependents() + } - /** Does set contain hash bits of given name? */ - def contains(name: Name): Boolean = { - val hash = name.hashCode & Mask - (bits(hash >> WordSizeLog) & (1L << hash)) != 0 + def apply(keepOnly: NameFilter, clsd: ClassDenotation)(implicit onBehalf: MemberNames, ctx: Context) = { + assert(isValid) + val cached = cache(keepOnly) + try + if (cached != null) cached + else { + locked = true + val computed = + try clsd.computeMemberNames(keepOnly)(this, ctx) + finally locked = false + cache = cache.updated(keepOnly, computed) + computed + } + finally addDependent(onBehalf) } + + def sameGroup(p1: Phase, p2: Phase) = p1.sameMembersStartId == p2.sameMembersStartId } - object FingerPrint { - def apply() = new FingerPrint(new Array[Long](NumWords)) - val unknown = new FingerPrint(null) - private final val WordSizeLog = 6 - private final val NumWords = 32 - private final val NumBits = NumWords << WordSizeLog - private final val Mask = NumBits - 1 + private class BaseDataImpl(createdAt: Period) extends InheritedCacheImpl(createdAt) with BaseData { + private[this] var cache: (List[ClassSymbol], BaseClassSet) = null + + private[this] var valid = true + private[this] var locked = false + private[this] var provisional = false + + final def isValid(implicit ctx: Context): Boolean = valid && isValidAt(ctx.phase) + + def invalidate(): Unit = + if (valid && !locked) { + cache = null + valid = false + invalidateDependents() + } + + def signalProvisional() = provisional = true + + def apply(clsd: ClassDenotation)(implicit onBehalf: BaseData, ctx: Context) + : (List[ClassSymbol], BaseClassSet) = { + assert(isValid) + try { + if (cache != null) cache + else { + if (locked) throw CyclicReference(clsd) + locked = true + provisional = false + val computed = + try clsd.computeBaseData(this, ctx) + finally locked = false + if (!provisional) cache = computed + else onBehalf.signalProvisional() + computed + } + } + finally addDependent(onBehalf) + } + + def sameGroup(p1: Phase, p2: Phase) = p1.sameParentsStartId == p2.sameParentsStartId } - private val AccessorOrLabel = Accessor | Label + class BaseClassSet(val classIds: Array[Int]) extends AnyVal { + def contains(sym: Symbol): Boolean = { + val id = sym.id + var lo = 0 + var hi = classIds.length - 1 + while (lo <= hi) { + val mid = (lo + hi) / 2 + if (id < classIds(mid)) hi = mid - 1 + else if (id > classIds(mid)) lo = mid + 1 + else return true + } + false + } + } @sharable private var indent = 0 // for completions printing } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index e0d9aca2bbc0..8b9f96781033 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -343,7 +343,7 @@ trait Symbols { this: Context => copy.denot = odenot.copySymDenotation( symbol = copy, owner = ttmap1.mapOwner(odenot.owner), - initFlags = odenot.flags &~ (Frozen | Touched) | Fresh, + initFlags = odenot.flags &~ Touched | Fresh, info = completer, privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here. annotations = odenot.annotations) @@ -467,6 +467,8 @@ object Symbols { if (this is Module) this.moduleClass.validFor |= InitialPeriod } else this.owner.asClass.ensureFreshScopeAfter(phase) + if (!this.flagsUNSAFE.is(Private)) + assert(phase.changesMembers, i"$this entered in ${this.owner} at undeclared phase $phase") entered } @@ -560,27 +562,6 @@ object Symbols { final def classDenot(implicit ctx: Context): ClassDenotation = denot.asInstanceOf[ClassDenotation] - private var superIdHint: Int = -1 - - override def superId(implicit ctx: Context): Int = { - val hint = superIdHint - if (hint >= 0 && hint <= ctx.lastSuperId && (ctx.classOfId(hint) eq this)) - hint - else { - val id = ctx.superIdOfClass get this match { - case Some(id) => - id - case None => - val id = ctx.nextSuperId - ctx.superIdOfClass(this) = id - ctx.classOfId(id) = this - id - } - superIdHint = id - id - } - } - override protected def prefixString = "ClassSymbol" } diff --git a/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala b/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala index f2ffaff5da61..1cca3fea5af5 100644 --- a/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala +++ b/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala @@ -31,6 +31,8 @@ import ast.Trees._ class AugmentScala2Traits extends MiniPhaseTransform with IdentityDenotTransformer with FullParameterization { thisTransform => import ast.tpd._ + override def changesMembers = true + override def phaseName: String = "augmentScala2Traits" override def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context) = NoSymbol diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala index 4f8c7cfceac8..d8499369d051 100644 --- a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -14,7 +14,7 @@ import core.StdNames.nme /** This phase translates arguments to call-by-name parameters, using the rules * - * x ==> x if x is a => parameter + * x ==> x if x is a => parameter * e.apply() ==> (e) if e is pure * e ==> (() => e) for all other arguments * diff --git a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala index b9a9544f513f..61e3722d7ec5 100644 --- a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -18,6 +18,9 @@ import SymUtils._ import collection.{ mutable, immutable } import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet } +/** This phase translates variables that are captured in closures to + * heap-allocated refs. + */ class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisTransform => import ast.tpd._ diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index 9a768e464483..1a05dc9f48b8 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -29,10 +29,11 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati override def phaseName = "elimRepeated" + override def changesMembers = true // the phase adds vararg bridges + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = elimRepeated(tp) - override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = super.transform(ref) match { case ref1: SymDenotation if (ref1 ne ref) && overridesJava(ref1.symbol) => diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index cc41ce00329e..b870d37c47b4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -37,6 +37,9 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => /** List of names of phases that should precede this phase */ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[InterceptedMethods], classOf[Splitter], classOf[ElimRepeated]) + override def changesMembers: Boolean = true // the phase adds bridges + override def changesParents: Boolean = true // the phase drops Any + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: SymDenotation => def isCompacted(sym: Symbol) = diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala index 8f99ef1d0805..e5f34e60e2af 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala @@ -44,6 +44,8 @@ class ExpandPrivate extends MiniPhaseTransform with IdentityDenotTransformer { t // This phase moves methods around (in infotransform) so it may need to make other methods public override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[MoveStatics]) + override def changesMembers = true // the phase introduces new members with mangled names + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { tree match { case t: DefDef => diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 60086031edff..937490ab060a 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -48,6 +48,8 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf */ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher], classOf[HoistSuperArgs]) + override def changesMembers = true // the phase adds outer accessors + /** Add outer accessors if a class always needs an outer pointer */ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { case tp @ ClassInfo(_, cls, _, decls, _) if needsOuterAlways(cls) && !sym.is(JavaDefined) => diff --git a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 61f32edaefff..02cf547b72e5 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -49,6 +49,8 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist + override def changesMembers = true // the phase adds extension methods + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass => moduleClassSym.linkedClass match { diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index a3cf71ef2203..dc85e6db736e 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -42,6 +42,8 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota private var addCompanionPhases: List[NeedsCompanions] = _ + override def changesMembers = true // the phase adds companion objects + def needsCompanion(cls: ClassSymbol)(implicit ctx: Context) = addCompanionPhases.exists(_.isCompanionNeeded(cls)) diff --git a/compiler/src/dotty/tools/dotc/transform/Flatten.scala b/compiler/src/dotty/tools/dotc/transform/Flatten.scala index f0104e715602..da287bfcac7f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Flatten.scala +++ b/compiler/src/dotty/tools/dotc/transform/Flatten.scala @@ -16,6 +16,8 @@ class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ override def phaseName = "flatten" + override def changesMembers = true // the phase removes inner classes + def transformSym(ref: SymDenotation)(implicit ctx: Context) = { if (ref.isClass && !ref.is(Package) && !ref.owner.is(Package)) { ref.copySymDenotation( diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index f64006d73c51..9afb3051478c 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -29,16 +29,8 @@ import Erasure.Boxing.adaptToType class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { import LazyVals._ - import tpd._ - def transformer = new LazyVals - - val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy - val initFlags = Flags.Synthetic | Flags.Method - - val containerFlagsMask = Flags.Method | Flags.Lazy | Flags.Accessor | Flags.Module - /** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions, * and number of bits currently used */ class OffsetInfo(var defs: List[Tree], var ord:Int) @@ -50,6 +42,15 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { * before this phase starts processing same tree */ override def runsAfter = Set(classOf[Mixin]) + override def changesMembers = true // the phase adds lazy val accessors + + def transformer = new LazyVals + + val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy + val initFlags = Flags.Synthetic | Flags.Method + + val containerFlagsMask = Flags.Method | Flags.Lazy | Flags.Accessor | Flags.Module + override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = transformLazyVal(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index f90778df009a..48037e89a29c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -98,6 +98,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + override def changesMembers = true // the phase adds implementions of mixin accessors + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) { val sym1 = diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 4472c9f5e170..4e12023a565f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -31,10 +31,8 @@ import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags -/** This transform eliminates patterns. Right now it's a dummy. - * Awaiting the real pattern matcher. - * elimRepeated is required - * TODO: outer tests are not generated yet. +/** This phase rewrites pattern matches. + * FIXME: A more detailed explanation would be good. */ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { import dotty.tools.dotc.ast.tpd._ diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 049ccb22e00d..d8a4b5d0c4af 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -58,6 +58,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran /** the following two members override abstract members in Transform */ override def phaseName: String = "posttyper" + override def changesMembers = true // the phase adds super accessors and synthetic methods + override def transformPhase(implicit ctx: Context) = thisTransformer.next protected def newTransformer(implicit ctx: Context): Transformer = diff --git a/compiler/src/dotty/tools/dotc/transform/PrimitiveForwarders.scala b/compiler/src/dotty/tools/dotc/transform/PrimitiveForwarders.scala index 7c51ba59388f..fec7bd741dab 100644 --- a/compiler/src/dotty/tools/dotc/transform/PrimitiveForwarders.scala +++ b/compiler/src/dotty/tools/dotc/transform/PrimitiveForwarders.scala @@ -37,6 +37,8 @@ class PrimitiveForwarders extends MiniPhaseTransform with IdentityDenotTransform override def runsAfter = Set(classOf[ResolveSuper]) + override def changesMembers = true // the phase adds primitive forwarders + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { val cls = impl.symbol.owner.asClass val ops = new MixinOps(cls, thisTransform) diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index de0242f79b27..64bcc4a63afd 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -53,6 +53,8 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th override def runsAfter = Set(classOf[ElimByName], // verified empirically, need to figure out what the reason is. classOf[AugmentScala2Traits]) + override def changesMembers = true // the phase adds super accessors and method forwarders + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { val cls = impl.symbol.owner.asClass val ops = new MixinOps(cls, thisTransform) diff --git a/compiler/src/dotty/tools/dotc/transform/RestoreScopes.scala b/compiler/src/dotty/tools/dotc/transform/RestoreScopes.scala index 8b9d2be0d021..8d2b8e25a61a 100644 --- a/compiler/src/dotty/tools/dotc/transform/RestoreScopes.scala +++ b/compiler/src/dotty/tools/dotc/transform/RestoreScopes.scala @@ -22,6 +22,8 @@ class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { t import ast.tpd._ override def phaseName = "restoreScopes" + override def changesMembers = true // the phase affects scopes, applying tree transformations of previous phases + /* Note: We need to wait until we see a package definition because * DropEmptyConstructors changes template members when analyzing the * enclosing package definitions. So by the time RestoreScopes gets to diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index ec82b5d33b74..91301c0cd86e 100644 --- a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -48,6 +48,9 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr import tpd._ override def phaseName: String = "shortcutImplicits" + + override def changesMembers = true // the phase adds "direct" methods + val treeTransform = new Transform /** If this option is true, we don't specialize symbols that are known to be only diff --git a/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala b/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala index b0bd40578c17..feacd2774b53 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -187,7 +187,7 @@ object TreeTransforms { else { val derivedAnnots = (annots, annotTrees1).zipped.map((annot, annotTree1) => annot.derivedAnnotation(annotTree1)) - ref1.copySymDenotation(annotations = derivedAnnots) + ref1.copySymDenotation(annotations = derivedAnnots).copyCaches(ref1, ctx.phase.next) } case ref1 => ref1 diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 1ce97098a61b..b8cf2dc4f286 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -911,7 +911,7 @@ class Namer { typer: Typer => Checking.checkWellFormed(cls) if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.info = avoidPrivateLeaks(cls, cls.pos) - cls.baseClasses.foreach(_.invalidateBaseTypeRefCache) + cls.baseClasses.foreach(_.invalidateBaseTypeRefCache) // we might have looked before and found nothing } }