@@ -757,95 +757,102 @@ object Denotations {
757757 * is brought forward to be valid in the new runId. Otherwise
758758 * the symbol is stale, which constitutes an internal error.
759759 */
760- def current (using Context ): SingleDenotation = {
760+ def current (using Context ): SingleDenotation =
761+ util.Stats .record(" current" )
761762 val currentPeriod = ctx.period
762763 val valid = myValidFor
763- if (valid.code <= 0 ) {
764- // can happen if we sit on a stale denotation which has been replaced
765- // wholesale by an installAfter; in this case, proceed to the next
766- // denotation and try again.
767- val nxt = nextDefined
768- if (nxt.validFor != Nowhere ) return nxt
769- assert(false , this )
770- }
771764
772- if (valid.runId != currentPeriod.runId)
773- if (exists) initial.bringForward().current
774- else this
775- else {
765+ def signalError () = println(s " error while transforming $this" )
766+
767+ def assertNotPackage (d : SingleDenotation , transformer : DenotTransformer ) = d match
768+ case d : ClassDenotation =>
769+ assert(! d.is(Package ), s " illegal transformation of package denotation by transformer $transformer" )
770+ case _ =>
771+
772+ def escapeToNext = nextDefined.ensuring(_.validFor != Nowhere )
773+
774+ def toNewRun =
775+ util.Stats .record(" current.bringForward" )
776+ if exists then initial.bringForward().current else this
777+
778+ def goForward =
776779 var cur = this
777- if (currentPeriod.code > valid.code) {
778- // search for containing period as long as nextInRun increases.
779- var next = nextInRun
780- while (next.validFor.code > valid.code && ! (next.validFor contains currentPeriod)) {
781- cur = next
782- next = next.nextInRun
783- }
784- if (next.validFor.code > valid.code) {
785- // in this case, next.validFor contains currentPeriod
786- cur = next
787- cur
788- }
789- else {
790- // println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod")
791- // not found, cur points to highest existing variant
792- val nextTransformerId = ctx.base.nextDenotTransformerId(cur.validFor.lastPhaseId)
793- if (currentPeriod.lastPhaseId <= nextTransformerId)
794- cur.validFor = Period (currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId)
795- else {
796- var startPid = nextTransformerId + 1
797- val transformer = ctx.base.denotTransformers(nextTransformerId)
798- // println(s"transforming $this with $transformer")
799- try
800- util.Stats .record(" denot transform" )
801- next = ctx.evalAt(transformer)(transformer.transform(cur))
802- // We temporarily update the context with the new phase instead of creating a
803- // new one. This is done for performance. We cut down on about 30% of context
804- // creations that way, and also avoid phase caches in contexts to get large.
805- // To work correctly, we need to demand that the context with the new phase
806- // is not retained in the result.
807- catch {
808- case ex : CyclicReference =>
809- println(s " error while transforming $this" ) // DEBUG
810- throw ex
811- }
812- if (next eq cur)
813- startPid = cur.validFor.firstPhaseId
814- else {
815- next match {
816- case next : ClassDenotation =>
817- assert(! next.is(Package ), s " illegal transformation of package denotation by transformer $transformer" )
818- case _ =>
819- }
820- next.insertAfter(cur)
821- cur = next
822- }
823- cur.validFor = Period (currentPeriod.runId, startPid, transformer.lastPhaseId)
824- // printPeriods(cur)
825- // println(s"new denot: $cur, valid for ${cur.validFor}")
826- }
827- cur.current // multiple transformations could be required
828- }
829- }
830- else {
831- // currentPeriod < end of valid; in this case a version must exist
832- // but to be defensive we check for infinite loop anyway
833- var cnt = 0
834- while (! (cur.validFor contains currentPeriod)) {
835- // println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}")
836- cur = cur.nextInRun
837- // Note: One might be tempted to add a `prev` field to get to the new denotation
838- // more directly here. I tried that, but it degrades rather than improves
839- // performance: Test setup: Compile everything in dotc and immediate subdirectories
840- // 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without.
841- cnt += 1
842- if (cnt > MaxPossiblePhaseId )
843- return atPhase(coveredInterval.firstPhaseId)(current)
844- }
780+ // search for containing period as long as nextInRun increases.
781+ var next = nextInRun
782+ while next.validFor.code > valid.code && ! (next.validFor contains currentPeriod) do
783+ cur = next
784+ next = next.nextInRun
785+ if next.validFor.code > valid.code then
786+ // in this case, next.validFor contains currentPeriod
787+ cur = next
845788 cur
846- }
847- }
848- }
789+ else
790+ // println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod")
791+ // not found, cur points to highest existing variant
792+ val nextTransformerId = ctx.base.nextDenotTransformerId(cur.validFor.lastPhaseId)
793+ if (currentPeriod.lastPhaseId <= nextTransformerId)
794+ cur.validFor = Period (currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId)
795+ else
796+ var startPid = nextTransformerId + 1
797+ val transformer = ctx.base.denotTransformers(nextTransformerId)
798+ // println(s"transforming $this with $transformer")
799+ val savedPeriod = ctx.period
800+ val mutCtx = ctx.asInstanceOf [FreshContext ]
801+ try
802+ mutCtx.setPhase(transformer)
803+ next = transformer.transform(cur)
804+ // We temporarily update the context with the new phase instead of creating a
805+ // new one. This is done for performance. We cut down on about 30% of context
806+ // creations that way, and also avoid phase caches in contexts to get large.
807+ // To work correctly, we need to demand that the context with the new phase
808+ // is not retained in the result.
809+ catch case ex : CyclicReference =>
810+ signalError()
811+ throw ex
812+ finally
813+ mutCtx.setPeriod(savedPeriod)
814+ if next eq cur then
815+ startPid = cur.validFor.firstPhaseId
816+ else
817+ assertNotPackage(next, transformer)
818+ next.insertAfter(cur)
819+ cur = next
820+ cur.validFor = Period (currentPeriod.runId, startPid, transformer.lastPhaseId)
821+ // printPeriods(cur)
822+ // println(s"new denot: $cur, valid for ${cur.validFor}")
823+ cur.current // multiple transformations could be required
824+ end goForward
825+
826+ def goBack : SingleDenotation =
827+ // currentPeriod < end of valid; in this case a version must exist
828+ // but to be defensive we check for infinite loop anyway
829+ var cur = this
830+ var cnt = 0
831+ while ! (cur.validFor contains currentPeriod) do
832+ // println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}")
833+ cur = cur.nextInRun
834+ // Note: One might be tempted to add a `prev` field to get to the new denotation
835+ // more directly here. I tried that, but it degrades rather than improves
836+ // performance: Test setup: Compile everything in dotc and immediate subdirectories
837+ // 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without.
838+ cnt += 1
839+ if cnt > MaxPossiblePhaseId then
840+ return atPhase(coveredInterval.firstPhaseId)(current)
841+ cur
842+ end goBack
843+
844+ if valid.code <= 0 then
845+ // can happen if we sit on a stale denotation which has been replaced
846+ // wholesale by an installAfter; in this case, proceed to the next
847+ // denotation and try again.
848+ escapeToNext
849+ else if valid.runId != currentPeriod.runId then
850+ toNewRun
851+ else if currentPeriod.code > valid.code then
852+ goForward
853+ else
854+ goBack
855+ end current
849856
850857 private def demandOutsideDefinedMsg (using Context ): String =
851858 s " demanding denotation of $this at phase ${ctx.phase}( ${ctx.phaseId}) outside defined interval: defined periods are ${definedPeriodsString}"
@@ -900,7 +907,7 @@ object Denotations {
900907
901908 /** Insert this denotation instead of `old`.
902909 * Also ensure that `old` refers with `nextInRun` to this denotation
903- * and set its `validFor` field to `NoWhere `. This is necessary so that
910+ * and set its `validFor` field to `Nowhere `. This is necessary so that
904911 * references to the old denotation can be brought forward via `current`
905912 * to a valid denotation.
906913 *
0 commit comments