#244 asdf operate chain on build/funcl: register-preloaded-system calls a non-function; defsystem name check-type sees NIL (residual after #243)
closedSixth layer of the operate-chain campaign (#239 #240 #241 #242 #243 all fixed & pushed). With defmethod dispatch working on build/funcl, the goal probe (asdf-probe: defsystem csys -> find-component -> load -> operate load-source-op, appended to a copy of asdf.lisp) still fails during/after the asdf load itself:
; Error: attempt to call an undefined function ; Error: There is no symbol ~S in package ~S | in: LET ; Error: attempt to call an object that is not a function | in: LET ; Error: attempt to call an object that is not a function | in: REGISTER-PRELOADED-SYSTEM ; Error: The value NIL is not a VECTOR (vector access). | in: IF (x3) ; Error: check-type failed: NIL is not of type COMMON-LISP:STRING | in: WITH-OPEN-FILE
register-preloaded-system runs at the END of the asdf load (asdf.lisp:14051, registering asdf/uiop as preloaded). Something it calls resolves to a non-function — possibly a mc-methods entry whose fn STILL restores as NIL through a path the #243 raw-pointer arm doesn't cover (closure? value-cell fn?), or an unrelated gf. The "no symbol ~S in package ~S" is new noise worth its own look (find-symbol/package layer). The NIL-vector accesses are downstream (accessor on a NIL instance). check-type NIL-not-STRING = the defsystem NAME lost on the way into register-system-definition.
Reference: the identical probe content works on stage2 (modulo probe-side terpri arity — funcl-stage2's terpri takes 0 args; build/funcl's takes a stream — that split is ALSO worth a look). Repro: /tmp/asdf-probe.lisp pattern = cp asdf.lisp + append probe writing marks to /tmp/diag.txt via write-string+princ-to-string (funcl's write/prin1 are 1-arg only).
Start per playbook: gdb break on the raise for the not-a-function in REGISTER-PRELOADED-SYSTEM, or instrument mc-dispatch to name the gf; check whether the called object is a restored mc-methods fn=NIL entry that predates the raw-pointer arm (i.e., a CLOSURE method lambda).
Comments (5)
REFRAME (full error census of the plain asdf load on build/funcl, post-230becd): the load that prints ASDF-LOAD-COMPLETE is NOT clean — it logs 28 compile/eval-phase errors that do NOT go through the raise trampoline (why the gdb raise-census caught zero; the '| in: NAME' suffix marks the compiling/evaluating form). Distinct failures, each its own target:
5x attempt to call an undefined function (no context) 3x invalid number of arguments: G13 expected 1, got 0 | in: WITH-UPGRADABILITY <- lifted-lambda ARITY bug, likely the juiciest 3x The value NIL is not a VECTOR (vector access). | in: IF 2x There is no symbol ~S in package ~S | in: LET 1x G13 expected 1, got 0 | in: OR 1x The value "SYSTEM-" is not a VECTOR | in: DEFINE-SYSTEM-VIRTUAL-SLOT-READERS 1x the value is not a sequence | in: DEFPARAMETER WILD-INFERIORS 1x ; Error: No package named UIOP | in: WITH-UPGRADABILITY (+ the remainder repeats of the above)
The register-preloaded-system / defsystem probe failures are downstream of whichever of these leaves rps/coerce-name/component machinery half-defined. GOOD NEWS from the same run: all 12 former runtime not-a-function raises (wild-path, the VERSION pair, the pathname-parser cluster) are GONE — healed by 230becd's designator fallback.
Suggested attack order for a fresh session: (1) G13 arity in with-upgradability (a lifted lambda called with 0 args — reproduces at load, names itself); (2) the 5 undefined-function calls (census them with a message-filtered bp on the UNDEFINED-funcall raise instead of not-a-function); (3) 'no symbol ~S in package ~S' (find-symbol layer). Full plain-load stdout with all 28 errors preserved in the gdb log pattern (rerun: gdb census script /tmp/244-c2.py against /tmp/asdf-in.lisp).
Round 3 landed (18b5e27) + CORRECTED ANALYSIS of the arity error:
LANDED: gensym-counter restore — the restored REPL restarted ag-gensym-counter at 0 while the restored fn-table already held baked G13 (offset 50872); fresh loads re-minted colliding lifted-lambda names (arity error when arities differ, SILENT WRONG-LAMBDA when they match). Counter now advances past the restored maximum during the fn-table walk. Suite 8/8.
CORRECTED: the census "G13 expected 1, got 0" did NOT clear — it shifted to G38 unchanged, so it is INTRA-LOAD, not a bake collision. Two decisive facts: (1) a gdb breakpoint on FUNCL-RAISE-ARITY-ERROR never fires during the whole load — the message is a COMPILE-TIME diagnostic (the '| in:
PRIME SUSPECT: uiop's while-collecting (asdf.lisp:1320, uiop/utility = the first error region) — its expansion's flet collectors become lifted lambdas under the hosted expander; a capture-carrying collector lifted to 1 param but call sites emitted with 0 args = the lost-capture shape. Its uses (3915, 8600, 12248, 12378, 12717) need mapping against the four error regions.
NEXT RECIPES: (a) make the compiler's arity diagnostic print the CALL FORM + ag-debug-source-path line (find the emitter that assembles 'invalid number of arguments: ~ expected ~, got ~' in the compile path — grep src/cold for the assembly of that message); (b) standalone repro: defmacro X using while-collecting-style flet collectors + a with-upgradability-style eval-when wrapper, loaded on build/funcl; (c) the census greps: full plain-load stdout, '; Error' lines — 28 baseline, distinct shapes listed in the previous comment.
Layer 7 narrowed (2026-07-07 session, post #245/#246/#247/#249/#250 — census now 19): the two check-type NILs + three "no accessor" tail errors all reduce to ONE fact:
(function component-name) resolves to a runtime-JIT-compiled function (fn-ptr 0x40D0B012 in this run) that returns NIL for ANY argument — called on the preloaded-system instance OR on a fixnum, no error, NIL. Meanwhile every table is healthy in the same image:
- (mc-dispatch (quote component-name) (list system)) => "uiop" (method entries + ancestor walk fine)
- (slot-value system (quote name)) => "uiop" (mc-accs fine)
- (mc-slot-value-safe system (quote name)) => "uiop"
So register-system's (check-type (component-name system) string) sees NIL because asdf's notinline late-bound call sites (funcall (function component-name) …) fetch this constant-NIL-behaving cell value, NOT the mc dispatcher and NOT the defclass reader (a dispatcher errors "no applicable method" on a fixnum; the reader errors "no accessor" — this thing silently returns NIL for both).
Candidates eliminated: fmakunbound stubs (%unbound-marker; the when-upgrading loop doesn't hit component-name), define-system-virtual-slot-reader (system-* names only), initarg matching (slot HAS the value).
NEXT: name the 0x40D0B012 function — reverse-lookup the fn-table by offset, or land #231 (JIT DWARF for gensym-named defuns) and read it from gdb; then find which load-time form installed it into the component-name cell. Probe files: /tmp/244-probe{,2,3}.lisp (each = load copy + 3-4 direct calls; ~4 min per run).
FIXED in 7ed84cf (layer 7 resolved). Root cause of the register-preloaded-system cluster: asdf's (define-condition bad-system-name … (name :reader component-name)) and (system-out-of-date … :reader component-name) clobber the component accessor by design, and the shared-name reader's non-condition fallback was DEAD — %mc-accessor-fallback guarded on mc-slot-value-hook, whose bake-time function value does not survive save-image/restore (read back NIL). Every (component-name system) returned constant NIL.
Fix: stdlib call sites now probe mini-clos presence via ag-fn-lookup (funcl-funcall-fallback pattern) and funcall the NAME. Bonus: mc-print-hook had the same disease — restored images print instances as # again instead of ? / raw vectors.
5-second standalone repro (no asdf): (defclass hc () ((nm :initarg :nm :accessor hc-nm))) + (define-condition hcond (condition) ((nm :initarg :nm :reader hc-nm))) → (hc-nm (make-instance …)) was NIL, now 7.
Census: 18 -> 14. Gone: both check-type NILs (register-preloaded-system + the dolist LET) and all three "no accessor" errors. register-preloaded-system registers "asdf"/"asdf-package-system"/"uiop" cleanly.
Goal probe update: defsystem no longer dies on the old failures — it now fails one layer deeper with "index 0 out of bounds | in: DEFSYSTEM", find-system => NIL. Next blocker = the dead uiop/pathname region (parse-unix-namestring / parse-native-namestring cells hold non-funcallable ? objects; fn-table has entries but ag-runtime-fn-addr-lookup misses; whole block died SILENTLY). Filing that as its own issue — closing this one.
Progress + narrowing (230becd pushed):
LANDED: funcl-funcall-fallback — funcall/apply of a symbol designator with an empty fn cell now resolves through the name-keyed runtime fn-table (and heals the cell), closing the whole designator door-split class. Suite 8/8, hosted 42, fixed point.
NARROWED: the register-preloaded-system failure is NOT that class — its raise is 'not a function' (the cons-callee-with-non-fn-car guards in emit-ir-funcall's closure path), not the empty-cell 'undefined function' path. So the callee is a DATA CONS (or malformed closure) — e.g. an unevaluated (lambda ...) list, a (SETF X) name, or a restored closure whose car lost its fn-ptr (the cave#243 raw-pointer arm covers plain code ptrs but a closure CONS serialized through the heap walker gets its car via the generic slot encoder — if that car was an anonymous fn it NOW encodes as a raw lowtag-2 vaddr and should decode fine post-10fc0eb... unless the closure was built at bake over captured env).
NEXT (fresh session): re-run the RAISE census filtering ONLY 'not a function' (skip wild-path + the fixed ones), decode the CALLEE cons at the closure-path guard (r14/rcx hold the cell), and map it back to the source form. Probe files: /tmp/244-census.py, /tmp/244-callee.py (adjust the trampoline vaddr for the current binary via (%mem-ref-u64 (+ 16777216 216))). Also still open from the description: the 'no symbol ~S in package ~S' error and the stage2-vs-funcl terpri arity split.