smalltalk: super send + top-level temps + 9 super tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 03:15:39 +00:00
parent 45147bd8a6
commit 82bad15b13
5 changed files with 218 additions and 20 deletions

View File

@@ -296,20 +296,41 @@
((st-class-ref? v) "Class")
(else (st-class-of v)))))
;; super send: lookup starts at the *defining* class's superclass, not the
;; receiver class. This is what makes inherited methods compose correctly
;; under refinement — a method on Foo that calls `super bar` resolves to
;; Foo's superclass's `bar` regardless of the dynamic receiver class.
(define
st-super-send
(fn
(receiver selector args defining-class)
(let
((super (st-class-superclass defining-class)))
(cond
((= super nil)
(error (str "super send past Object: " selector)))
(else
(let ((method (st-method-lookup super selector false)))
(cond
((not (= method nil)) (st-invoke method receiver args))
(else (st-primitive-send receiver selector args)))))))))
(cond
((= defining-class nil)
(error (str "super send outside method context: " selector)))
(else
(let
((super (st-class-superclass defining-class))
(class-side? (st-class-ref? receiver)))
(cond
((= super nil)
(error (str "super send past root: " selector " in " defining-class)))
(else
(let ((method (st-method-lookup super selector class-side?)))
(cond
((not (= method nil))
(st-invoke method receiver args))
(else
;; Try primitives starting from super's perspective too —
;; for native receivers the primitive table is global, so
;; super basically reaches the same primitives. The point
;; of super is to skip user overrides on the receiver's
;; class chain below `super`, which method-lookup above
;; already enforces.
(let ((p (st-primitive-send receiver selector args)))
(cond
((= p :unhandled)
(st-dnu receiver selector args))
(else p)))))))))))))
;; ── Method invocation ──────────────────────────────────────────────────
(define
@@ -689,7 +710,13 @@
(src)
(let
((ast (st-parse src)) (frame (st-make-frame nil nil nil)))
(let ((result (smalltalk-eval-ast ast frame)))
(cond
((st-return-marker? result) (get result :value))
(else result))))))
(begin
(when
(and (dict? ast) (has-key? ast :temps))
(for-each
(fn (t) (dict-set! (get frame :locals) t nil))
(get ast :temps)))
(let ((result (smalltalk-eval-ast ast frame)))
(cond
((st-return-marker? result) (get result :value))
(else result)))))))