Fix record field-reorder redefine (jolt-wf4) and builtin-shadowing local names (jolt-fjb1) (#124)

* Don't direct-link a var redefined earlier in the same unit (jolt-wf4)

defrecord/deftype expands to (do (def R (make-deftype-ctor ...)) (def ->R R) ...),
so the ->R alias references R within one compiled unit. Under direct-link a var
ref embeds the cell's root as a compile-time constant, but on a redefine R's old
root is still in place when that unit compiles — the (def R new) sibling hasn't
run yet — so ->R sealed to the stale pre-redef ctor. (defrecord R [x a])
(defrecord R [a x]) (:a (->R 10 20)) read the old [x a] layout and returned 20.

Track the vars a unit (re)defines and force their later in-unit references to the
live indirect deref. The cell is registered only after its own init is emitted,
so a recursive self-reference inside the init still direct-links (it runs after
the def completes); only sibling references after the def go indirect.

* Emit Janet's `in` as a value so a user local can't shadow it (jolt-fjb1)

The back end emits `in` to deref var cells ((in cell :root)) and index
shape-recs. It emitted the bare symbol, so a user local named `in` shadowed
Janet's builtin in the surrounding scope and the generated cell-deref called the
user's value as a function — "<table> called with 2 arguments, possibly expected
1". malli's explainer binds [value in acc], so m/explain hit this on every
schema (m/validate was unaffected — its path doesn't bind `in`).

Embed `in`'s function value at the emit sites (as jolt-call/core-get already
are); a value in head position can't be shadowed. Fixes m/explain on malli
(loaded with JOLT_FEATURES=clj so its .cljc reader-conditionals resolve).

---------

Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
Dmitri Sotnikov 2026-06-15 16:46:00 +00:00 committed by GitHub
parent 747ac87e03
commit 8ee04eed8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 10 deletions

View file

@ -58,6 +58,19 @@
(let [ctx (init {:compile? true :aot-core? false})]
(check "aot-core off still correct" (eval-string ctx "(last [1 2 3])") 3))
# 6. Redefining a record with REORDERED fields must rebind the ctor (jolt-wf4).
# deftype expands to (do (def R (make-deftype-ctor ...)) (def ->R R) ...): the
# `->R` alias references R in the SAME compiled unit. Direct-link embeds a var's
# root as a compile-time constant, but R's redefined root hasn't RUN yet when
# that unit compiles — so ->R must NOT seal to R's stale (pre-redef) ctor.
(let [ctx (init {:compile? true :direct-linking? true})]
(eval-string ctx "(defrecord R [x a])")
(eval-string ctx "(defrecord R [a x])")
(check "record field-reorder redefine: :a reads the new layout"
(eval-string ctx "(:a (->R 10 20))") 10)
(check "record field-reorder redefine: :x reads the new layout"
(eval-string ctx "(:x (->R 10 20))") 20))
(if (pos? failures)
(do (printf "direct-linking: %d failure(s)" failures) (os/exit 1))
(print "direct-linking: all matrix cases passed"))