A frame whose slots are all the compiler's own is compared against the slot count its record carries, so a frame that prints keeps its globals and locals

This commit is contained in:
Joseph Ferano 2026-09-25 22:26:11 +07:00
parent 285bb1047f
commit c41d27ec45
4 changed files with 26 additions and 19 deletions

View File

@ -1657,9 +1657,6 @@ CLOSED: [2026-09-25]
** TODO The inspector holds a value
Reading needs no module now, so an address and a type are enough to keep a value on the daemon's side between requests, the way CIDER keeps a JVM object. Nothing holds one yet: the Emacs stack is still a stack of expressions.
** TODO A frame that prints is skipped from the globals section
A frame whose body calls =print= comes back in =:skipped= as "running a body that has been redefined since" though nothing was redefined: its global-reference fingerprint differs between the build and the daemon. test/programs/dev-parity.flan stores each global to itself instead of printing it for this reason.
** DONE The watch table stays pushed
CLOSED: [2026-09-25]
The watch table stays pushed, and shares the push channel program output moves to.

View File

@ -2588,11 +2588,11 @@ let stopped_frame t ~frame ~what : (string * Tast.fn, string) result =
up" is a claim about the body this session holds, and a
zero-slot frame whose body has since been replaced by one
with slots is a frame that claim is false about. *)
if nslots <> Array.length fn.Tast.slots then
if nslots <> Emit.recorded_slots fn then
Error
(Printf.sprintf
"%s on the stack has %d slots and the %s this session holds has %d — the frame is running a body that has been redefined since"
name nslots name (Array.length fn.Tast.slots))
name nslots name (Emit.recorded_slots fn))
else if sig_ <> Emit.slot_fingerprint fn then
(* The count matching is not the same as the body matching.
A redefinition that renames a local, or changes its type
@ -2628,7 +2628,7 @@ let eval_expr ?frame ?at_stop t ~code ~origin ~pause =
(* A frame with no slots has no locals to bind, and the program
has no table to answer for it: the expression sees globals. *)
let bound =
if Array.length fn.Tast.slots = 0 then Ok []
if Emit.recorded_slots fn = 0 then Ok []
else bound_slots t ~frame:index
in
(* [at_stop] is the stop the editor drew the frame at. It is not
@ -2677,7 +2677,7 @@ let locals t ~frame =
match stopped_frame t ~frame ~what:"locals" with
| Error m -> error m
| Ok (name, fn) ->
if Array.length fn.Tast.slots = 0 then
if Emit.recorded_slots fn = 0 then
ok
[ ":frame " ^ Wire.quote name; ":locals ()"; ":refused ()";
":note " ^ Wire.quote "this frame has no named locals" ]
@ -3457,7 +3457,7 @@ let globals_op t =
"not a function this session holds; a lifted handler clause \
has no declaration of its own to read references from"
| Some fn ->
if nslots <> Array.length fn.Tast.slots then
if nslots <> Emit.recorded_slots fn then
skip
"the frame is running a body that has been redefined \
since, so what this session holds is a different body's \

View File

@ -2022,6 +2022,17 @@ let slot_fingerprint (fn : Tast.fn) =
fn.Tast.slots;
Hashtbl.hash (Buffer.contents b) land 0x3fffffff
(* How many slots a frame's record says it has: all of them when any is named,
and none otherwise, since only a function with a named slot gets a slot
table (see the shadow stack's push in [emit_fn]). Both backends write it and
[Dev] compares a frame against it, so a body whose slots are all the
compiler's own — a [print]'s temporaries — reads as the same body at both
ends. *)
let recorded_slots (fn : Tast.fn) =
if Array.exists (fun n -> n <> None) fn.Tast.snames then
Array.length fn.Tast.slots
else 0
let fninfo m (fn : Tast.fn) ~nslots =
let nid, nlen = fi_bytes m fn.Tast.name in
let lid, llen = fi_bytes m (Loc.to_string fn.Tast.floc) in

View File

@ -54,18 +54,17 @@
(defonce pair (Pair i32))
;; The innermost frame names every global above, so the break loop's section
;; holds all of them; then it stops. Each is stored back to itself rather than
;; printed: a frame that prints is refused attribution today (TODO.org, "A
;; frame that prints is skipped from the globals section"), and this is about
;; the values.
;; holds all of them; then it stops. It prints them, and a frame whose only
;; slots are the printer's temporaries is still attributed its globals.
(defn inner [] i64
(set small small) (set mid mid) (set large large) (set huge huge)
(set neg neg) (set ratio ratio) (set far far) (set odd odd) (set yes yes)
(set byte byte) (set text text) (set colour colour) (set stray stray)
(set some some) (set none none) (set wide wide) (set deep deep)
(set dot dot) (set empty empty) (set row row) (set words words)
(set nums nums) (set live live) (set dead dead) (set nowhere nowhere)
(set un un) (set anything anything) (set pair pair)
(print small) (print mid) (print large) (print huge)
(print neg) (print ratio) (print far) (print odd) (print yes)
(print byte) (print text) (print colour) (print stray)
(print some) (print none) (print wide) (print deep)
(print dot) (print empty) (print row) (print words)
(print nums) (print live) (print dead) (print nowhere)
(print un) (print anything) (print pair)
(println "")
(error (Boom {.why 3}))
0)