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 ** 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. 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 ** DONE The watch table stays pushed
CLOSED: [2026-09-25] CLOSED: [2026-09-25]
The watch table stays pushed, and shares the push channel program output moves to. 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 up" is a claim about the body this session holds, and a
zero-slot frame whose body has since been replaced by one zero-slot frame whose body has since been replaced by one
with slots is a frame that claim is false about. *) 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 Error
(Printf.sprintf (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" "%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 else if sig_ <> Emit.slot_fingerprint fn then
(* The count matching is not the same as the body matching. (* The count matching is not the same as the body matching.
A redefinition that renames a local, or changes its type 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 (* A frame with no slots has no locals to bind, and the program
has no table to answer for it: the expression sees globals. *) has no table to answer for it: the expression sees globals. *)
let bound = 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 else bound_slots t ~frame:index
in in
(* [at_stop] is the stop the editor drew the frame at. It is not (* [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 match stopped_frame t ~frame ~what:"locals" with
| Error m -> error m | Error m -> error m
| Ok (name, fn) -> | Ok (name, fn) ->
if Array.length fn.Tast.slots = 0 then if Emit.recorded_slots fn = 0 then
ok ok
[ ":frame " ^ Wire.quote name; ":locals ()"; ":refused ()"; [ ":frame " ^ Wire.quote name; ":locals ()"; ":refused ()";
":note " ^ Wire.quote "this frame has no named locals" ] ":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 \ "not a function this session holds; a lifted handler clause \
has no declaration of its own to read references from" has no declaration of its own to read references from"
| Some fn -> | Some fn ->
if nslots <> Array.length fn.Tast.slots then if nslots <> Emit.recorded_slots fn then
skip skip
"the frame is running a body that has been redefined \ "the frame is running a body that has been redefined \
since, so what this session holds is a different body's \ 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; fn.Tast.slots;
Hashtbl.hash (Buffer.contents b) land 0x3fffffff 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 fninfo m (fn : Tast.fn) ~nslots =
let nid, nlen = fi_bytes m fn.Tast.name in let nid, nlen = fi_bytes m fn.Tast.name in
let lid, llen = fi_bytes m (Loc.to_string fn.Tast.floc) in let lid, llen = fi_bytes m (Loc.to_string fn.Tast.floc) in

View File

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