diff --git a/TODO.org b/TODO.org index c1920e68..fa59faf1 100644 --- a/TODO.org +++ b/TODO.org @@ -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. diff --git a/lib/dev.ml b/lib/dev.ml index 94983a82..33e10e3a 100644 --- a/lib/dev.ml +++ b/lib/dev.ml @@ -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 \ diff --git a/lib/emit.ml b/lib/emit.ml index fd4f6423..0ea86354 100644 --- a/lib/emit.ml +++ b/lib/emit.ml @@ -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 diff --git a/test/programs/dev-parity.flan b/test/programs/dev-parity.flan index f91e2993..277838a6 100644 --- a/test/programs/dev-parity.flan +++ b/test/programs/dev-parity.flan @@ -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)