@@ -716,7 +716,10 @@ object Denotations {
716
716
this match {
717
717
case symd : SymDenotation =>
718
718
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()
720
723
case _ =>
721
724
}
722
725
if (! symbol.exists) return updateValidity()
@@ -757,89 +760,102 @@ object Denotations {
757
760
* is brought forward to be valid in the new runId. Otherwise
758
761
* the symbol is stale, which constitutes an internal error.
759
762
*/
760
- def current (using Context ): SingleDenotation = {
763
+ def current (using Context ): SingleDenotation =
764
+ util.Stats .record(" current" )
761
765
val currentPeriod = ctx.period
762
766
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
- }
771
767
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 =
776
782
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
839
791
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
843
859
844
860
private def demandOutsideDefinedMsg (using Context ): String =
845
861
s " demanding denotation of $this at phase ${ctx.phase}( ${ctx.phaseId}) outside defined interval: defined periods are ${definedPeriodsString}"
@@ -894,7 +910,7 @@ object Denotations {
894
910
895
911
/** Insert this denotation instead of `old`.
896
912
* 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
898
914
* references to the old denotation can be brought forward via `current`
899
915
* to a valid denotation.
900
916
*
0 commit comments