From b97a3dbbf13d0a895f1098445c6c6d214a9dec18 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 12 Sep 2026 13:36:34 +0700 Subject: [PATCH] The inspector walks expressions, which is why it cannot see a frame --- DISCUSS.md | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/DISCUSS.md b/DISCUSS.md index 77e958e..fcc281b 100644 --- a/DISCUSS.md +++ b/DISCUSS.md @@ -8,20 +8,42 @@ Settled decisions live in `NEXT.md`. Reasons for what already exists live in `BU --- -## 1. What is `i` supposed to inspect in the stack section? +## 1. `i`, the inspector, and the frame it cannot see -Raised as a question about intent, and it deserves one, because the current answer may be wrong rather than merely -undocumented. +Two things got conflated here and they should be separated. -`i` on a local passes that local's **name** to the inspector, which evaluates it as an expression inside the running -program. That is correct for the innermost frame, where the name is in scope. **It is not obviously correct for any -other frame** — evaluating `y` while looking at frame 3 evaluates `y` wherever the evaluator stands, not in frame 3. -It may resolve to a global, to a different binding, or fail. +**What the inspector already does.** Most of what was asked for is built. `flan-inspect` opens its own buffer, lays a +value's fields one per line, `RET` walks into one, `l` comes back, `g` re-reads. The renderer bounds its walk at depth 4 +and span 8, and entering a field renders *that field* from depth 0 — so the elision moves with you rather than +truncating permanently. It is CIDER's inspector adapted, and the file says what the adaptation changed. -The locals *listing* does not have this problem: it renders from the frame's own slot addresses. So the display is -frame-accurate and the inspector may not be. Options: root the inspector at the slot's address the way the listing is -rooted; refuse `i` outside the innermost frame; or make the inspector frame-aware. Worth settling before anyone relies -on it. +**What is actually missing** is detail on the leaves: a number shows in decimal only, with no hex and no binary, and a +pointer does not show its address. Purely additive, small, and worth doing. + +**The real problem, and it is sharper than "a bug".** The locals listing renders from each frame's own slot addresses, +so it is frame-accurate. The inspector is built on a stack of **expressions** — going into a field means sending a +different expression (`(.pos b)` where the last was `b`), and `l` works by popping back to the previous one. That design +is forced: a Flan value has no header, the thunk that rendered it is `dlclose`d as soon as it returns, and there is no +heap to retain anything in, so nothing can be held server-side the way CIDER holds a JVM object. + +The consequence is that `i` evaluates a name wherever the evaluator stands, **not in the frame being looked at**. On the +innermost frame that happens to be right. On any other it may resolve to a global, to a different binding, or fail — +with nothing saying so. + +**An earlier suggestion in this conversation — "root the inspector at the slot's address" — does not work**, and the +reason is worth keeping: an address is not an expression, so the first `RET` has nothing to build the next expression +from and navigation dies at step one. Recorded because it is the obvious fix and it is wrong. + +So the options are genuinely three, and none is free: + +1. **Teach the program to evaluate an expression relative to a frame.** The most useful and the most work: the frame's + slots would have to be in scope for a compiled thunk, which means the daemon building a thunk whose free names bind + to that frame's addresses. It would also fix `C-x C-e` while stopped, which has the same blindness. +2. **Give the inspector a second rooting mode** — an address root that can still walk, by carrying a type alongside the + address and stepping to a field's address rather than to a sub-expression. Navigation then works, but the two modes + have different capabilities and `l` has to cross between them. +3. **Refuse `i` outside the innermost frame**, honestly and by name. Cheapest, and it gives up the feature exactly where + it is most wanted, since the innermost frame is the one already fully visible. ## 2. Annotating the IR and the disassembly with the source