@@ -286,7 +286,7 @@ object Contexts {
286286 * contexts are created only on request and cached in this array
287287 */
288288 private var phasedCtx : Context = this
289- private var phasedCtxs : Array [Context ] = _
289+ private var phasedCtxs : Array [Context ] = null
290290
291291 /** This context at given phase.
292292 * This method will always return a phase period equal to phaseId, thus will never return squashed phases
@@ -340,11 +340,6 @@ object Contexts {
340340 /** The current reporter */
341341 def reporter : Reporter = typerState.reporter
342342
343- /** Run `op` as if it was run in a fresh explore typer state, but possibly
344- * optimized to re-use the current typer state.
345- */
346- final def test [T ](op : Context ?=> T ): T = typerState.test(op)(using this )
347-
348343 /** Is this a context for the members of a class definition? */
349344 def isClassDefContext : Boolean =
350345 owner.isClass && (owner ne outer.owner)
@@ -453,15 +448,17 @@ object Contexts {
453448 /** Is the explicit nulls option set? */
454449 def explicitNulls : Boolean = base.settings.YexplicitNulls .value
455450
456- protected def init (outer : Context , origin : Context ): this .type = {
457- util.Stats .record(" Context.fresh" )
451+ /** Initialize all context fields, except typerState, which has to be set separately
452+ * @param outer The outer context
453+ * @param origin The context from which fields are copied
454+ */
455+ private [Contexts ] def init (outer : Context , origin : Context ): this .type = {
458456 _outer = outer
459457 _period = origin.period
460458 _mode = origin.mode
461459 _owner = origin.owner
462460 _tree = origin.tree
463461 _scope = origin.scope
464- _typerState = origin.typerState
465462 _typeAssigner = origin.typeAssigner
466463 _gadt = origin.gadt
467464 _searchHistory = origin.searchHistory
@@ -472,11 +469,19 @@ object Contexts {
472469 this
473470 }
474471
472+ def reuseIn (outer : Context ): this .type =
473+ implicitsCache = null
474+ phasedCtxs = null
475+ sourceCtx = null
476+ init(outer, outer)
477+
475478 /** A fresh clone of this context embedded in this context. */
476479 def fresh : FreshContext = freshOver(this )
477480
478481 /** A fresh clone of this context embedded in the specified `outer` context. */
479- def freshOver (outer : Context ): FreshContext = new FreshContext (base).init(outer, this )
482+ def freshOver (outer : Context ): FreshContext =
483+ util.Stats .record(" Context.fresh" )
484+ FreshContext (base).init(outer, this ).setTyperState(this .typerState)
480485
481486 final def withOwner (owner : Symbol ): Context =
482487 if (owner ne this .owner) fresh.setOwner(owner) else this
@@ -539,25 +544,59 @@ object Contexts {
539544 * of its attributes using the with... methods.
540545 */
541546 class FreshContext (base : ContextBase ) extends Context (base) {
542- def setPeriod (period : Period ): this .type = { this .period = period; this }
543- def setMode (mode : Mode ): this .type = { this .mode = mode; this }
544- def setOwner (owner : Symbol ): this .type = { assert(owner != NoSymbol ); this .owner = owner; this }
545- def setTree (tree : Tree [? >: Untyped ]): this .type = { this .tree = tree; this }
547+ def setPeriod (period : Period ): this .type =
548+ util.Stats .record(" Context.setPeriod" )
549+ this .period = period
550+ this
551+ def setMode (mode : Mode ): this .type =
552+ util.Stats .record(" Context.setMode" )
553+ this .mode = mode
554+ this
555+ def setOwner (owner : Symbol ): this .type =
556+ util.Stats .record(" Context.setOwner" )
557+ assert(owner != NoSymbol )
558+ this .owner = owner
559+ this
560+ def setTree (tree : Tree [? >: Untyped ]): this .type =
561+ util.Stats .record(" Context.setTree" )
562+ this .tree = tree
563+ this
546564 def setScope (scope : Scope ): this .type = { this .scope = scope; this }
547- def setNewScope : this .type = { this .scope = newScope; this }
565+ def setNewScope : this .type =
566+ util.Stats .record(" Context.setScope" )
567+ this .scope = newScope
568+ this
548569 def setTyperState (typerState : TyperState ): this .type = { this .typerState = typerState; this }
549570 def setNewTyperState (): this .type = setTyperState(typerState.fresh().setCommittable(true ))
550571 def setExploreTyperState (): this .type = setTyperState(typerState.fresh().setCommittable(false ))
551572 def setReporter (reporter : Reporter ): this .type = setTyperState(typerState.fresh().setReporter(reporter))
552- def setTypeAssigner (typeAssigner : TypeAssigner ): this .type = { this .typeAssigner = typeAssigner; this }
573+ def setTypeAssigner (typeAssigner : TypeAssigner ): this .type =
574+ util.Stats .record(" Context.setTypeAssigner" )
575+ this .typeAssigner = typeAssigner
576+ this
553577 def setTyper (typer : Typer ): this .type = { this .scope = typer.scope; setTypeAssigner(typer) }
554- def setGadt (gadt : GadtConstraint ): this .type = { this .gadt = gadt; this }
578+ def setGadt (gadt : GadtConstraint ): this .type =
579+ util.Stats .record(" Context.setGadt" )
580+ this .gadt = gadt
581+ this
555582 def setFreshGADTBounds : this .type = setGadt(gadt.fresh)
556- def setSearchHistory (searchHistory : SearchHistory ): this .type = { this .searchHistory = searchHistory; this }
557- def setSource (source : SourceFile ): this .type = { this .source = source; this }
583+ def setSearchHistory (searchHistory : SearchHistory ): this .type =
584+ util.Stats .record(" Context.setSearchHistory" )
585+ this .searchHistory = searchHistory
586+ this
587+ def setSource (source : SourceFile ): this .type =
588+ util.Stats .record(" Context.setSource" )
589+ this .source = source
590+ this
558591 def setTypeComparerFn (tcfn : Context => TypeComparer ): this .type = { this .typeComparer = tcfn(this ); this }
559- private def setMoreProperties (moreProperties : Map [Key [Any ], Any ]): this .type = { this .moreProperties = moreProperties; this }
560- private def setStore (store : Store ): this .type = { this .store = store; this }
592+ private def setMoreProperties (moreProperties : Map [Key [Any ], Any ]): this .type =
593+ util.Stats .record(" Context.setMoreProperties" )
594+ this .moreProperties = moreProperties
595+ this
596+ private def setStore (store : Store ): this .type =
597+ util.Stats .record(" Context.setStore" )
598+ this .store = store
599+ this
561600 def setImplicits (implicits : ContextualImplicits ): this .type = { this .implicitsCache = implicits; this }
562601
563602 def setCompilationUnit (compilationUnit : CompilationUnit ): this .type = {
@@ -632,14 +671,42 @@ object Contexts {
632671 final def retractMode (mode : Mode ): c.type = c.setMode(c.mode &~ mode)
633672 }
634673
674+ /** Test `op` in a fresh context with a typerstate that is not committable.
675+ * The passed context may not survive the operation.
676+ */
677+ def explore [T ](op : Context ?=> T )(using Context ): T =
678+ util.Stats .record(" Context.test" )
679+ val base = ctx.base
680+ import base ._
681+ val nestedCtx =
682+ if testsInUse < testContexts.size then
683+ testContexts(testsInUse).reuseIn(ctx)
684+ else
685+ val ts = TyperState ()
686+ .setReporter(TestingReporter ())
687+ .setCommittable(false )
688+ val c = FreshContext (ctx.base).init(ctx, ctx).setTyperState(ts)
689+ testContexts += c
690+ c
691+ testsInUse += 1
692+ val nestedTS = nestedCtx.typerState
693+ nestedTS.init(ctx.typerState, ctx.typerState.constraint)
694+ val result =
695+ try op(using nestedCtx)
696+ finally
697+ nestedTS.reporter.asInstanceOf [TestingReporter ].reset()
698+ testsInUse -= 1
699+ result
700+ end explore
701+
635702 /** A class defining the initial context with given context base
636703 * and set of possible settings.
637704 */
638705 private class InitialContext (base : ContextBase , settingsGroup : SettingGroup ) extends FreshContext (base) {
639706 outer = NoContext
640707 period = InitialPeriod
641708 mode = Mode .None
642- typerState = new TyperState ( null )
709+ typerState = TyperState .initialState( )
643710 owner = NoSymbol
644711 tree = untpd.EmptyTree
645712 typeAssigner = TypeAssigner
@@ -785,6 +852,9 @@ object Contexts {
785852
786853 protected [dotc] val indentTab : String = " "
787854
855+ private [dotc] val testContexts = new mutable.ArrayBuffer [FreshContext ]
856+ private [dotc] var testsInUse : Int = 0
857+
788858 def reset (): Unit = {
789859 for ((_, set) <- uniqueSets) set.clear()
790860 errorTypeMsg.clear()
0 commit comments