flan/test/programs/dev-robust.flan
Joseph Ferano fef6ae04f6 A failed build is a refusal, and a refusal leaves the session standing
The daemon caught Loc.Error at each op and nothing else. That was survivable
while the frontend was the only thing that could refuse a form; it is not now
that expansion is part of evaluating. Both C-c C-c and C-x C-e run a clang
driver through Build.macro_module, which answers with an exit status and a
Failure, and a dlopen that finds no symbol answers with another one. Neither
is a Loc.Error, so neither was answered, and an exception past serve is not a
refused evaluation — it is a dead daemon with the program still on screen and
a closed socket waiting for the editor's next request.

The boundary is now one place, around the whole of a request, rather than a
new arm at each of the dozens of calls. Out_of_memory, Stack_overflow and
Sys.Break go through it: those say the process cannot continue, and answering
"error" to them would claim a session survived something it did not.
Everything else is about the form that was sent, and the message it carries
is the one the user can act on, so a clang exit status reaches :message
instead of being flattened to "internal error".

The session's own state goes with it. Session.eval wrote the imported macro
set above the checker, so a form that did not check left the session holding
a package's macros and none of its declarations; it is held and committed at
the bottom with decls, program and env. Session.eval_expr committed the
generic copies it had instantiated before emitting the module that carries
them, which is the session believing it holds a body nothing was written for;
that assignment moved below Emit.

Both are pinned. test_session drives the two rollbacks in process, and
test_dev drives a real daemon whose macro module cannot be built — the
expression path and the redefinition path, each followed by the same
evaluation succeeding and by the session still knowing the program.
2026-09-13 19:43:18 +07:00

29 lines
1.2 KiB
Plaintext

;;;; A program to send failing evaluations at, for as long as it takes.
;;;;
;;;; Same shape as dev-repl.flan and for the same reason — C-x C-e is a thunk
;;;; the agent runs at a frame boundary, so a program under test has to keep
;;;; reaching them — but with room to spare. What test_dev.ml drives here is
;;;; the *failing* path, and a failure in this loop costs a clang driver that a
;;;; success does not: the macro module is built from scratch, thrown away, and
;;;; built again. dev-repl.flan's 4000 frames are twenty seconds, which is less
;;;; than that sequence takes on a cold cache, and a program that ran out mid
;;;; test would look exactly like the session death the test is here to deny.
;;;;
;;;; Two minutes, then, against a block that runs in well under one: enough
;;;; margin for a cold machine, and short enough that an aborted run does not
;;;; leave a process of this behind for the rest of the afternoon.
(import agent "vendor:agent")
(defvar ticks i64)
(defn step [] i64
(set ticks (+ ticks 1))
ticks)
(defn main [] i32
(agent/start "/tmp/flan-dev-robust-fallback.sock")
(dotimes [i 24000]
(agent/wait 5)
(set ticks (step)))
0)