reflective: extract class-chain.sx — Smalltalk + CLOS method dispatch share parent-walk
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 35s

lib/guest/reflective/class-chain.sx — class inheritance walker with
adapter cfg for single-parent (Smalltalk) and multi-parent (CLOS)
hierarchies. Three primitives:

- refl-class-chain-find-with CFG CN PROBE
    DFS through parents, returns first non-nil probe result.
    Smalltalk method lookup uses this.

- refl-class-chain-depth-with CFG CN ANCESTOR
    Min hop distance via any parent path, or nil if unreachable.
    CLOS method specificity uses this.

- refl-class-chain-ancestors-with CFG CN
    Flat DFS-ordered list of all reachable ancestor names.

Adapter cfg has two keys: :parents-of (CN → list of parent names,
possibly empty) and :class? (predicate; short-circuits walk on
non-existent class names mid-chain).

Migrations:
- lib/smalltalk/runtime.sx: st-method-lookup-walk now a 9-line
  thin probe through the kit (was 20 lines of inline recursion);
  st-class-cfg wraps the single-parent :superclass field into a
  1-element list for the cfg.

- lib/common-lisp/clos.sx: clos-specificity is a one-line wrapper
  around refl-class-chain-depth-with (was 28 lines); clos-class-cfg
  reads the multi-parent :parents field.

Both consumers green:
- Smalltalk: 847/847 (unchanged)
- CL: 222/240 (unchanged baseline; 18 pre-existing failures, all
  in stdlib functions like cl-set-memberp, unrelated to CLOS).

This is the second extracted reflective kit (env.sx was first).
The adapter-cfg pattern continues to bridge structurally divergent
consumers (Smalltalk single-inheritance vs CLOS multiple-inheritance
with method-precedence distance) via a uniform :parents-of callback.
This commit is contained in:
2026-05-12 21:09:07 +00:00
parent 9efbf4ad38
commit 2981a479e8
6 changed files with 177 additions and 53 deletions

View File

@@ -221,30 +221,37 @@
(st-ic-bump-generation!)
true))))))))))
;; Smalltalk-side adapter for lib/guest/reflective/class-chain.sx.
;; Smalltalk has single inheritance: :parents-of returns a 1-element
;; list (or empty) wrapping the single :superclass field.
(define st-class-cfg
{:parents-of (fn (cn)
(let ((p (st-class-superclass cn)))
(cond ((nil? p) (list))
(:else (list p)))))
:class? (fn (n) (st-class-exists? n))})
;; Walk-only lookup. Returns the method record (with :defining-class) or nil.
;; class-side? = true searches :class-methods, false searches :methods.
;; Parent-chain walk delegates to refl-class-chain-find-with; the probe
;; tests this class's method dict and returns the entry (or nil).
(define
st-method-lookup-walk
(fn
(cls-name selector class-side?)
(let
((found nil))
(begin
(define
ml-loop
(fn
(cur)
(when
(and (= found nil) (not (= cur nil)) (st-class-exists? cur))
(let
((c (st-class-get cur)))
(let
((dict (if class-side? (get c :class-methods) (get c :methods))))
(cond
((has-key? dict selector) (set! found (get dict selector)))
(else (ml-loop (get c :superclass)))))))))
(ml-loop cls-name)
found))))
(fn (cls-name selector class-side?)
(refl-class-chain-find-with
st-class-cfg
cls-name
(fn (cn)
(let ((c (st-class-get cn)))
(cond
((nil? c) nil)
(:else
(let ((dict (if class-side?
(get c :class-methods)
(get c :methods))))
(cond
((has-key? dict selector) (get dict selector))
(:else nil))))))))))
;; Cached lookup. Misses are stored as :not-found so doesNotUnderstand paths
;; don't re-walk on every send.