@@ -716,7 +716,10 @@ object Denotations {
716716 this match {
717717 case symd : SymDenotation =>
718718 if (stillValid(symd)) return updateValidity()
719- if (acceptStale(symd)) return symd.currentSymbol.denot.orElse(symd).updateValidity()
719+ if acceptStale(symd) && symd.initial.validFor.firstPhaseId <= ctx.lastPhaseId then
720+ // New run might have fewer phases than old, so symbol might no longer be
721+ // visible at all. TabCompleteTests have examples where this happens.
722+ return symd.currentSymbol.denot.orElse(symd).updateValidity()
720723 case _ =>
721724 }
722725 if (! symbol.exists) return updateValidity()
@@ -757,89 +760,102 @@ object Denotations {
757760 * is brought forward to be valid in the new runId. Otherwise
758761 * the symbol is stale, which constitutes an internal error.
759762 */
760- def current (using Context ): SingleDenotation = {
763+ def current (using Context ): SingleDenotation =
764+ util.Stats .record(" current" )
761765 val currentPeriod = ctx.period
762766 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- }
771767
772- if (valid.runId != currentPeriod.runId)
773- if (exists) initial.bringForward().current
774- else this
775- else {
768+ def signalError () = println(s " error while transforming $this" )
769+
770+ def assertNotPackage (d : SingleDenotation , transformer : DenotTransformer ) = d match
771+ case d : ClassDenotation =>
772+ assert(! d.is(Package ), s " illegal transformation of package denotation by transformer $transformer" )
773+ case _ =>
774+
775+ def escapeToNext = nextDefined.ensuring(_.validFor != Nowhere )
776+
777+ def toNewRun =
778+ util.Stats .record(" current.bringForward" )
779+ if exists then initial.bringForward().current else this
780+
781+ def goForward =
776782 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- next = atPhase(transformer)(transformer.transform(cur))
801- catch {
802- case ex : CyclicReference =>
803- println(s " error while transforming $this" ) // DEBUG
804- throw ex
805- }
806- if (next eq cur)
807- startPid = cur.validFor.firstPhaseId
808- else {
809- next match {
810- case next : ClassDenotation =>
811- assert(! next.is(Package ), s " illegal transformation of package denotation by transformer $transformer" )
812- case _ =>
813- }
814- next.insertAfter(cur)
815- cur = next
816- }
817- cur.validFor = Period (currentPeriod.runId, startPid, transformer.lastPhaseId)
818- // printPeriods(cur)
819- // println(s"new denot: $cur, valid for ${cur.validFor}")
820- }
821- cur.current // multiple transformations could be required
822- }
823- }
824- else {
825- // currentPeriod < end of valid; in this case a version must exist
826- // but to be defensive we check for infinite loop anyway
827- var cnt = 0
828- while (! (cur.validFor contains currentPeriod)) {
829- // println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}")
830- cur = cur.nextInRun
831- // Note: One might be tempted to add a `prev` field to get to the new denotation
832- // more directly here. I tried that, but it degrades rather than improves
833- // performance: Test setup: Compile everything in dotc and immediate subdirectories
834- // 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without.
835- cnt += 1
836- if (cnt > MaxPossiblePhaseId )
837- return atPhase(coveredInterval.firstPhaseId)(current)
838- }
783+ // search for containing period as long as nextInRun increases.
784+ var next = nextInRun
785+ while next.validFor.code > valid.code && ! (next.validFor contains currentPeriod) do
786+ cur = next
787+ next = next.nextInRun
788+ if next.validFor.code > valid.code then
789+ // in this case, next.validFor contains currentPeriod
790+ cur = next
839791 cur
840- }
841- }
842- }
792+ else
793+ // println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod")
794+ // not found, cur points to highest existing variant
795+ val nextTransformerId = ctx.base.nextDenotTransformerId(cur.validFor.lastPhaseId)
796+ if currentPeriod.lastPhaseId <= nextTransformerId then
797+ cur.validFor = Period (currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId)
798+ else
799+ var startPid = nextTransformerId + 1
800+ val transformer = ctx.base.denotTransformers(nextTransformerId)
801+ // println(s"transforming $this with $transformer")
802+ val savedPeriod = ctx.period
803+ val mutCtx = ctx.asInstanceOf [FreshContext ]
804+ try
805+ mutCtx.setPhase(transformer)
806+ next = transformer.transform(cur)
807+ // We temporarily update the context with the new phase instead of creating a
808+ // new one. This is done for performance. We cut down on about 30% of context
809+ // creations that way, and also avoid phase caches in contexts to get large.
810+ // To work correctly, we need to demand that the context with the new phase
811+ // is not retained in the result.
812+ catch case ex : CyclicReference =>
813+ signalError()
814+ throw ex
815+ finally
816+ mutCtx.setPeriod(savedPeriod)
817+ if next eq cur then
818+ startPid = cur.validFor.firstPhaseId
819+ else
820+ assertNotPackage(next, transformer)
821+ next.insertAfter(cur)
822+ cur = next
823+ cur.validFor = Period (currentPeriod.runId, startPid, transformer.lastPhaseId)
824+ // printPeriods(cur)
825+ // println(s"new denot: $cur, valid for ${cur.validFor}")
826+ cur.current // multiple transformations could be required
827+ end goForward
828+
829+ def goBack : SingleDenotation =
830+ // currentPeriod < end of valid; in this case a version must exist
831+ // but to be defensive we check for infinite loop anyway
832+ var cur = this
833+ var cnt = 0
834+ while ! (cur.validFor contains currentPeriod) do
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 then
843+ return atPhase(coveredInterval.firstPhaseId)(current)
844+ cur
845+ end goBack
846+
847+ if valid.code <= 0 then
848+ // can happen if we sit on a stale denotation which has been replaced
849+ // wholesale by an installAfter; in this case, proceed to the next
850+ // denotation and try again.
851+ escapeToNext
852+ else if valid.runId != currentPeriod.runId then
853+ toNewRun
854+ else if currentPeriod.code > valid.code then
855+ goForward
856+ else
857+ goBack
858+ end current
843859
844860 private def demandOutsideDefinedMsg (using Context ): String =
845861 s " demanding denotation of $this at phase ${ctx.phase}( ${ctx.phaseId}) outside defined interval: defined periods are ${definedPeriodsString}"
@@ -894,7 +910,7 @@ object Denotations {
894910
895911 /** Insert this denotation instead of `old`.
896912 * Also ensure that `old` refers with `nextInRun` to this denotation
897- * and set its `validFor` field to `NoWhere `. This is necessary so that
913+ * and set its `validFor` field to `Nowhere `. This is necessary so that
898914 * references to the old denotation can be brought forward via `current`
899915 * to a valid denotation.
900916 *
0 commit comments