flan/test/programs/dev-locals.flan
Joseph Ferano 831cab9fb2 Review follow-ups: x86 parity, ArithError, and three refusal bugs
Five fixes off the independent review, plus the author's u8 ruling.

x86 parity: the bad-index block always ran x86 (it is flan dev's
default) and now says so with an explicit --x86; the condition render
gets an assertion under the x86 backend too, beside the LLVM one, and
a user error is pinned as carrying no site on both.

ArithError's layout is now pinned: {i32 op; i64 lhs, rhs} in C against
the prelude's defstruct, read field by field through the break loop's
render, driven from the editor through a divide under a restart-case.
That also covers condition and site on LLVM.

Three refusals that were wrong: trap_site tested the prefix "err"
and so ate any site whose path began with those letters; source_line
let Sys_error from input_line escape and take the whole break reply
with it, leaking the handle; and a condition with no fields was
reported as a name no struct has. The daemon now sends its own field
count and the buffer tells the two empties apart.

Nits taken: an over-long site is dropped rather than silently
truncated into a plausible one; the caret pads with the source line's
own tabs; the headline says when it has cut the field list;
flan-cnr-layout is live again as the single spelling of that request
rather than dead beside an inlined copy.

And the ruling: a u8 renders as 97 (\a) where a person is inspecting
and stays 97 where the program is printing.
2026-09-20 22:55:11 +07:00

51 lines
2.3 KiB
Plaintext

;;;; A program that stops with something worth looking at in the frame.
;;;;
;;;; dev-break.flan proves an editor can find out *that* a program stopped and
;;;; choose a restart; this one is about what the frame holds while it is
;;;; stopped. One local of each shape the structural printer has an arm for —
;;;; a parameter, a string, a struct, a fixed array, a bool — plus one that is
;;;; bound only *after* the error, which is the case that must come back
;;;; refused rather than rendered: its slot is storage nothing has written yet.
(import agent "vendor:agent")
(defstruct Point [x f32 y f32])
(defstruct Boom [why i32])
(defn look [n i64 label string] i64
(let [p (Point {.x 1.5 .y 2.5})
xs [10 20 30]
flag (> n 0)
;; Three bytes, for the three answers the character half has. A [u8]
;; already renders as text, so a lone byte showing only its number is
;; the one place the same data reads two ways — but only where a
;; person is inspecting. [println] of a u8 stays a number.
byte (u8 97) ; printable: 97 (\a)
gap (u8 32) ; one the reader names: 32 (\space)
ctl (u8 7)] ; no spelling: the number alone
;; A loop, for its hidden bound: [dotimes] allocates a slot nobody named,
;; and the listing must *hide* it rather than refuse it by an invented
;; name — [s6] is not a variable anyone can find in this file.
(dotimes [hop 0] (print ""))
;; And a shadowing rebind. The checker suffixes the repeat as [label~2]
;; so the debug info never claims one binding is the other; the listing
;; keeps both raw spellings, because two rows both called [label] with
;; nothing to tell them apart would be worse.
(let [label "inner"]
(restart-case
(do (error (Boom {.why 7}))
;; Never reached before the break, so [after] is a slot with nothing
;; in it: the frame records a null for it and this is what "not bound
;; yet" has to mean.
(let [after (i64 99)] after))
(carry-on [] 5)))))
(defvar ticks i64)
(defn main [] i32
(agent/start "/tmp/flan-dev-locals-fallback.sock")
(print (look 3 "hello")) (println "")
(dotimes [i 4000]
(agent/wait 5)
(set ticks (+ ticks 1)))
0)