#250 uiop/stream: registering the with-output defmacro (locally + handler-bind wrapper) hits "attempt to call an undefined function | in: LET"
closedasdf.lisp:4132-4140: (with-upgradability () (locally (declare …) (handler-bind (#+sbcl …) (defmacro with-output (…) …)))) errors at load with "; Error: attempt to call an undefined function | in: LET" and with-output never gets defined.
REPL probes that PASS, ruling out the obvious suspects: (locally (declare) …) works; (handler-bind () …) works. So the undefined callee is something else on the load path of this exact nesting — needs the arity/undefined-fn gdb trap to name it (same recipe as cave#245: redefine the raiser to spin, attach, walk the stack).
Consequence: with-output consumers degrade later in the load.
Found triaging the 24 residual asdf-load errors under #205.
Comments (2)
Fixed in cd6aae7: ir-lower-call lowers expression-position DEFMACRO to (progn (ag-register-defmacro (quote FORM)) (quote NAME)) — same ag-macros entry the toplevel pass writes, lazy expander compile unchanged. Minimal repro passes; (locally (declare) (handler-bind () (defmacro wo2 …))) registers and expands. Census: 20 -> 19, the "| in: LET" undefined-function at the uiop with-output block is gone. Suite note: new dispatch case only — no existing emit path changed; self-compile fixed point held.
Root-caused, no load needed. Minimal repro:
The loader walks progn/locally/eval-when bodies and handles defmacro forms it finds, but handler-bind is a stdlib MACRO whose expansion buries the body under (let (…) … (unwind-protect (progn ,@body) …)) — the defmacro lands in compiled code, where the compiler has no lowering for it (emits an unresolved call).
uiop hits this because with-upgradability wraps the with-output defmacro in (locally (declare …) (handler-bind (#+sbcl …) (defmacro with-output …))) — the #+sbcl-stripped handler-bind is semantically a no-op but still swallows the defmacro.
Proper fix is compiler work: lower non-toplevel DEFMACRO to a registration call (compile the expander lambda + call the same machinery the loader dispatch uses; ag-defmacro-form-p / the REPL defmacro path). Same lowering would presumably cover defun-in-handler-bind etc. Leaving open as the marker for that work.