flan/test/programs/dyn-defer.flan
Joseph Ferano bd981ff87a Four dyn values in a defer the collector had never been told about
A defer is in the typed IR twice -- spliced into the body for the normal path,
and again in fdefers for the path a transfer leaves through -- so a dyn
temporary inside one is emitted twice. dyn_roots counted only the body's, and
the second copy went into slots nothing had rooted.

Nothing failed, and that is the whole reason this is worth a commit of its own.
dyn_tmp falls back to a plain slot rather than unbalancing the stack, so the
pushes and the pops still matched, the program ran and printed the right answer,
and the values were simply invisible. Against a stub that never collects there is
no symptom to find -- no leak, no crash, no wrong number. It would have become a
symptom the week the real collector landed, in a defer reached only on a handled
condition, which is close to the worst place to start looking.

What found it was the IR: a rooted slot is spelled %dr and the fallback %dx, and
the assertion is that no dyn program in the corpus emits one of the latter. That
is now a test over all five dyn programs, and it is the only check in the lane
that can see a missing root while there is still nothing to lose one by. When
the collector arrives it is the thing to extend rather than replace.

Also checked, both clean: flan dev --llvm builds and runs a dyn program, which
is the route the x86 refusal sends people to and would have been a link error in
the worst possible place; and the daemon's own refusal already names the flag.
2026-09-19 06:40:53 +07:00

29 lines
1.1 KiB
Plaintext

;;;; A dyn value produced inside a defer, on a function a transfer leaves
;;;; through rather than returns from.
;;;;
;;;; This is here for the root count and not for the arithmetic. A defer appears
;;;; twice in the typed IR — spliced into the body for the normal path, and
;;;; again in fdefers for the path a handled condition unwinds along — so the
;;;; emitter produces two copies of every dyn temporary inside one. Counting
;;;; only the body left the second copy's temporaries in slots the collector had
;;;; never been told about: the pushes and the pops still balanced, so nothing
;;;; failed, and the values were simply invisible.
;;;;
;;;; Nothing the stub does can show that, because it never collects. What shows
;;;; it is the emitted IR — a fallback slot is spelled %dx and a rooted one %dr,
;;;; and the fix is the absence of the former.
(defstruct Boom [n i64])
(defn inner [x] dyn
(defer (print (+ x 1000)) (print "\n"))
(restart-case
(error (Boom {.n 1}))
(give [] 0))
(+ x 1))
(defn main [] ()
(handler-bind [(Boom [b] (invoke-restart 'give))]
(print (inner 5))
(print "\n")))