(name: string, def: BoxedDefinition)
| 143 | // silent revert for the pop to cover, and the pop must not advance `any` |
| 144 | // either. This is what keeps |
| 145 | // a read-only scoped probe — a `Comprehension` count/finiteness scan, a |
| 146 | // lazy `Filter` emptiness walk — from retiring every `_type`/`_sgn` cache |
| 147 | // engine-wide: those probes bracket with push/pop per read, and with an |
| 148 | // unconditional bump each probe invalidated the very caches the enclosing |
| 149 | // type derivation was filling, so boxing a row that references a |
| 150 | // comprehension-bound name recomputed the whole subtree per node (Tycho |
| 151 | // item 181: 872K clean pops and 1.85M wasted type recomputes in ONE |
| 152 | // canonical box, ~60–90 s per `.type` read). |
| 153 | const clean = |
| 154 | context?._assumptionsDirty !== true && |
| 155 | context?._anyVersionAtPush === ce._anyVersion && |
| 156 | context?._semanticVersionAtPush === ce._semanticVersion && |
| 157 | context?._worldVersionAtPush === ce._worldVersion; |
| 158 | ce._noteStateEvent({ |
| 159 | kind: 'scope-pop', |
| 160 | assumptionsDirty: context?._assumptionsDirty === true, |
| 161 | ...(clean ? { clean: true } : {}), |
| 162 | }); |
| 163 | } |
| 164 | |
| 165 | export function inScope<T>( |
| 166 | ce: IComputeEngine, |
| 167 | scope: Scope | undefined, |
| 168 | f: () => T |
| 169 | ): T { |
| 170 | if (!scope) return f(); |
| 171 | |
| 172 | // Push a temporary eval context to switch to the given scope |
| 173 | ce._evalContextStack.push({ |
| 174 | lexicalScope: scope, |
| 175 | name: '', |
| 176 | assumptions: new ExpressionMap(ce.context?.assumptions ?? []), |
| 177 | }); |
| 178 | |
| 179 | try { |
| 180 | // During boxing this temporary lexical scope is the correct restart owner |
| 181 | // for a devolved builtin shadow. Outside boxing, `withScopedRepair()` is a |
| 182 | // transparent single pass, so evaluation side effects are never repeated. |
| 183 | return ce._boxingState.withScopedRepair(scope, f); |
| 184 | } finally { |
| 185 | const popped = ce._evalContextStack.pop(); |
| 186 | // This transient pop bypasses `discardEvalContext`, so it runs the |
| 187 | // checkpoint frame-retirement hook itself: a checkpoint taken inside |
| 188 | // this extent stood on the popped frame and dies with it. |
| 189 | if (popped !== undefined && ce._checkpointStack.length > 0) |
| 190 | ce._invalidateCheckpointsOnFrameDiscard(popped); |
no test coverage detected