From 03d4460d72311fcee3fedcb28ee02171cf3ea2f5 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 12 Sep 2026 17:18:44 +0700 Subject: [PATCH] WIP: the inspector's address root, half wired on the Emacs side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OCaml side is done and builds. Emacs side is mid-edit and INCOMPLETE — see the handoff below. `dune build --root . @check` is green. `dune test --root .` was NOT run. The .el files were not byte-compiled and flan-inspect.el will not work as it stands: the state layer still speaks the old single-expression shape while the helpers above it have been rewritten for roots and paths. WHAT WORKS (daemon, lib/, in the parent commit and unchanged here) - `(:op "inspect" :frame N :slot I :path (...))` renders one value rooted at a stopped frame's slot address. `Session.render_slot` is `render_locals` with a path applied to the root before the walk and one line out instead of one per slot; no second walk was written and no backend change was needed. - A path step is a string for a struct field, an integer for an array or slice element, and the symbol `some` for an option's payload. A union case field is spelled `Union.case.field`, because the payload's offset depends on the case and only the renderer knows which case the value is in. - Every step that does not fit the type in hand is refused by name with the reason: a field the type does not have, an index past a fixed array's end, `some` on something that is not an option, a union field without its case. - The frame's identity IS checked, and not by a second copy: `Dev.stopped_frame` is one function now and `locals` and `inspect` both go through it — alive, stopped, frame exists, the frame is the program's and not a thunk's, the body is one this session holds, the slot count matches, and `Emit.slot_fingerprint` matches. `inspect` additionally refuses an unbound slot, for the listing's reason: a null address would fault on the stopped game thread. - The slot travels by INDEX, not by name. Two slots can share a name (`fresh_slot` only allocates) and a refused slot is not in the listing, so neither the name nor the position identifies one. `locals` now puts the slot index as a fourth element on each `:locals` entry. - `Dev.run_render_thunk` is one function; `locals`, `globals` and `inspect` share the build/deliver/wait/read tail. - `layout`'s "union values are milestone 6" is corrected. WHAT IS HALF-BUILT, AND EXACTLY WHERE IT STOPS `emacs/flan-inspect.el`. Done: the header comment explaining the two roots; `flan-inspect-step-expr` taking a 3-element `:field` step; `flan-inspect-wire-step`; `flan-inspect--root-label`; `flan-inspect-refusal` taking an optional ROOT and allowing an option's payload under a `:slot` root. NOT done, and this is the whole of what is left: 1. `flan-inspect--expr` / `flan-inspect--stack` still hold a bare expression. They must become `flan-inspect--root` (`(:expr EXPR)` or `(:slot FRAME SLOT NAME)`) plus `flan-inspect--path`, with stack entries of `(ROOT PATH . POINT)`. 2. `flan-inspect--value` must branch on the root: `eval-expr` with `(flan-inspect--root-label root path)` for `:expr`; for `:slot`, send `(:op "inspect" :frame F :slot S :path P)` with P built by `flan-inspect-wire-step` over the path, and take `:value` from the reply. 3. `flan-inspect--show`, `-into`, `-pop`, `-refresh` rewired to (ROOT PATH). `-into` must build a `:some` step when the node's kind is `option`, and put the parent node's `:type` as the third element of a `:field` step. 4. New entry point `flan-inspect-slot (frame slot name)`, kept separate from `flan-inspect (expr)` — `emacs/flan-mode.el` autoloads and binds the latter and that file is out of this lane. 5. `emacs/flan-cnr.el`: the `flan-cnr-inspect` text property must carry `(:slot FRAME SLOT NAME)` on a local line — the slot index is `(nth 3 l)` now — and `(:expr NAME)` on a global line, with `flan-cnr-inspect` dispatching to the right entry point. 6. `emacs/test-flan-cider.el`: the fixture at "`i' on a local inspects it by name" asserts the old behaviour and must be rewritten; the locals fixtures need a fourth element. 7. `test/test_dev.ml`: no coverage of the new op yet. The discriminating test to write first is a stack whose OUTER frame has a local whose name is also a global with a different value, asserting `inspect` answers the frame's value. A new `test/programs/dev-inspect.flan` is picked up by the existing glob. 8. `BUILT.md`, `emacs/MANUAL.md`, and striking the item from `NEXT.md`'s "Decided in discussion" and `DISCUSS.md` item 1 — none done. THE THREE ANSWERS THE TASK ASKED FOR - Navigation in the new mode: the daemon supports it fully — RET extends the path, `l` shortens it, and both are a fresh request, so the view is never stale. The Emacs half of that is item 3 above and is not wired. - `l` does not cross between the modes, and that is structural rather than a rule: a stack entry carries its own root, RET only ever extends the path under the root it already has, and every new root starts with an empty stack. A mixed stack cannot be constructed, so the question does not arise — and it stays answered if a third rooting mode is added. - What each mode cannot do that the other can. The expression root works on a RUNNING program and roots at anything you can write, a call included; it cannot name a frame, so it is the bug. The slot root names one frame and one slot and is exact; it reaches an option's payload and a union case's fields, which have offsets but no accessor in the surface language; it needs a stopped program, it is refused when the frame's body was redefined since it was entered, and it cannot root at an expression at all. --- emacs/flan-inspect.el | 112 +++++++++++++++++++++++++++++++++++++++-- emacs/flan-inspect.elc | Bin 0 -> 19147 bytes 2 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 emacs/flan-inspect.elc diff --git a/emacs/flan-inspect.el b/emacs/flan-inspect.el index 0e246d6..9d2a666 100644 --- a/emacs/flan-inspect.el +++ b/emacs/flan-inspect.el @@ -33,6 +33,47 @@ ;; (lib/session.ml). A field past either bound comes back as `...' and no ;; amount of squinting at the echo area recovers it. Re-rooting the walk at ;; that field renders it from depth 0 — the bound moves with you. +;; +;;; Two ways to root a walk, and why there had to be a second +;; +;; Everything above describes the *expression* root, and it has one hole: an +;; expression is evaluated where the evaluator stands. `i' on a local in the +;; break buffer used to send that local's name, and on the innermost frame +;; that lands in the right frame by luck. On any other it may resolve to a +;; global, to another binding of the same name, or to nothing — with the +;; locals listing right above it showing the frame's own storage, because that +;; listing renders from each frame's slot addresses and is frame-accurate. +;; The display was right and this buffer was not. +;; +;; Rooting at the slot's address alone does not fix it, and that was tried: +;; an address is not an expression, so the first RET has nothing to build +;; from. What the shadow stack changed is that the *step* does not have to be +;; an expression either. The daemon has the frame's address and every slot's +;; type, so going into a field is an address plus an offset with that field's +;; type — the arithmetic `Render.render' already does for the listing. So +;; there is a second rooting mode here, `flan-inspect-slot', and the daemon +;; verb behind it is `(:op "inspect" :frame N :slot I :path (...))'. +;; +;; The two roots are not equally capable and the buffer says which it is on: +;; +;; the expression root works on a *running* program and roots at anything +;; you can write, a call included. It cannot reach an option's payload, +;; because Flan has no accessor form that does, and it cannot say which +;; frame it means; +;; +;; the slot root names one frame and one slot, so it is exact, and it +;; reaches an option's payload and a union case's fields, which have offsets +;; but no accessor. It needs a stopped program, it is refused if the +;; frame's body was redefined since it was entered — the same slot +;; fingerprint the listing is refused by — and it cannot root at an +;; expression, so `g' after the program resumes is refused rather than +;; quietly answered from somewhere else. +;; +;; `l' never crosses between them, and that is structural rather than a rule: +;; a stack entry carries its own root, RET only ever extends the path under +;; the root it already has, and every new root — `flan-inspect', +;; `flan-inspect-slot' — starts with an empty stack. So a mixed stack cannot +;; be built, and that stays true if a third rooting mode is ever added. ;;; Code: @@ -219,27 +260,88 @@ reply without a daemon behind them, and so that this file names ;; work without a handle to retain. (defun flan-inspect-step-expr (expr step) - "The Flan expression reaching STEP inside EXPR." + "The Flan expression reaching STEP inside EXPR. +A `:field' step may carry the type it was read out of, for the slot root's +benefit; here it is ignored, because an accessor is written the same way +whatever the value came from." (pcase step - (`(:field ,name) (format "(.%s %s)" name expr)) + (`(:field ,name . ,_) (format "(.%s %s)" name expr)) (`(:index ,i) (format "(at %s %d)" expr i)) (_ expr))) +;;; Where a field is, said as an offset + +;; The slot root's version of the same step, and it is not source: the daemon +;; is walking a type, so a field is its name and an element is its number. +;; Two cases need more than the name. +;; +;; A union's payload sits at an offset that depends on which case the value +;; is in, and only the renderer knows which case it currently is — it wrote +;; `(Union.case {.f …})'. So the type travels with the step and the wire +;; spelling is `Union.case.f'. Guessing the case from a field name two +;; cases share would read one case's layout over another's payload. +;; +;; An option's payload has no name at all; it is the symbol `some'. + +(defun flan-inspect-wire-step (step) + "STEP as the `inspect' op spells it." + (pcase step + (`(:field ,name ,type) + (if (and (stringp type) (string-match-p "\\." type)) + (concat type "." name) + name)) + (`(:field ,name) name) + (`(:index ,i) i) + (`(:some) 'some) + (_ (format "%s" step)))) + +;;; A root, and the path walked from it + +;; A root is `(:expr EXPR)' or `(:slot FRAME SLOT NAME)'. The path is a list +;; of steps applied to it in order, and the pair is the whole of this buffer's +;; position — which is why a stack entry carries both and `l' cannot cross +;; between two kinds of root by accident. + +(defun flan-inspect--root-label (root path) + "How ROOT walked by PATH is named at the top of the buffer and in the trail." + (pcase root + (`(:expr ,expr) + (seq-reduce #'flan-inspect-step-expr path expr)) + (`(:slot ,frame ,_slot ,name) + (concat (format "%s [frame %d]" name frame) + (mapconcat (lambda (s) + (pcase s + (`(:field ,f . ,_) (concat "." f)) + (`(:index ,i) (format "[%d]" i)) + (`(:some) ".some") + (_ ""))) + path ""))) + (_ "?"))) + ;;; Why a thing cannot be entered ;; Every refusal is by name and carries its reason, because the alternative — ;; RET doing nothing on some lines and something on others — is a UI that ;; teaches you nothing about the language. -(defun flan-inspect-refusal (node) - "Why NODE cannot be inspected, or nil if it can." +(defun flan-inspect-refusal (node &optional root) + "Why NODE cannot be inspected, or nil if it can. +ROOT is the root the walk is on, because two of these are refusals of the +*expression* root rather than of the value. Omitted means the expression +root, which is the older and the more limited of the two." (pcase (plist-get node :kind) ('struct (and (null (plist-get node :children)) "a struct with no fields the renderer could reach")) ('seq (and (null (plist-get node :children)) "an empty sequence: there is no element to go into")) ('option - "an option's payload: Flan has no accessor form that reaches it, so there is no expression to send") + ;; The one place the two roots differ in the slot root's favour, and it + ;; is worth saying which it is rather than refusing flatly: the payload + ;; is field 1 and the compiler reaches it there, so an address root steps + ;; into it by offset. Nothing in the surface language does, so an + ;; expression root has nothing to send. + (and (not (eq (car-safe root) :slot)) + "an option's payload: Flan has no accessor form that reaches it, so there is no expression to send. `i' on a local in the break buffer roots at the slot's address instead, and that root can step into it")) ('ptr ;; And its address is not here either. `Render.render' (lib/render.ml) ;; writes the bare word `' for every pointer, on purpose: it is the diff --git a/emacs/flan-inspect.elc b/emacs/flan-inspect.elc new file mode 100644 index 0000000000000000000000000000000000000000..1dde00429d655daa618917dcfc58603d6a2600af GIT binary patch literal 19147 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 B2Iv3) literal 0 HcmV?d00001