fix: map literals evaluate in source order; land the local-callee inline

jolt-p3c: Clojure evaluates map-literal entries left to right, but the
reader represented map forms as bare janet structs, so entries ran in
hash order. The reader now carries [k v ...] source order out-of-band —
on a struct PROTOTYPE (keys/kvs/length ignore protos, so macros that
get/keys literal map forms see no change; jolt-equal? was already
structural) and as a plain field on the phm rep (nil key/value). The
analyzer (form-map-pairs), the interpreter's map eval, both
syntax-quote walks, and core-sqmap (the lowered `{...} builder — the
array-map case, where Clojure also preserves insertion order) all honor
it, so the order survives macroexpansion in both modes.

jolt-507 root-caused: the parked inline put a LOCAL in janet call-head
position for the first time, and janet resolves head symbols against
the macro table before lexical upvalues — clojure.core/repeat's
self-name local expanded as janet's (repeat n & body) macro, compiling
the self-call into a countdown loop returning nil. Everything in the
issue (interpose, interleave) traced to that one name collision. The
emitter now rebinds local callees to reserved _fp$ symbols (argument
positions never consult the macro table), and the inline — direct
calls for function locals, jolt-call only for IFn-collection
leftovers — lands. Spec rows pin locals named repeat/seq/with called
in head position.

Gate green, suite 4718 steady, bench even with main.
This commit is contained in:
Yogthos 2026-06-12 14:09:50 -04:00
parent 34d32ea3bf
commit 15d599c0f3
6 changed files with 110 additions and 22 deletions

View file

@ -35,10 +35,16 @@
["collection key" ":v" "(get {[1 2] :v} [1 2])"]
["collection value-equal key" ":v" "(get {[1 2] :v} (vector 1 2))"]
["computed key" "1" "(get {(keyword \"a\") 1} :a)"]
# NOTE: entry ORDER is reader-hash order today, not source order (Clojure
# evaluates array-map literals left-to-right) — pinned loosely; see jolt-p3c
["values evaluate exactly once each" "[1 2 3]"
"(do (def log (atom [])) {:a (swap! log conj 1) :b (swap! log conj 2) :c (swap! log conj 3)} (vec (sort (deref log))))"]
# jolt-p3c: literal entries evaluate left to right in SOURCE order (the
# reader carries [k v ...] order on a struct prototype / phm field)
["values evaluate in source order" "[1 2 3]"
"(do (def log (atom [])) {:a (swap! log conj 1) :b (swap! log conj 2) :c (swap! log conj 3)} (deref log))"]
["keys evaluate before their values, pairwise" "[:k1 :v1 :k2 :v2]"
"(do (def log (atom [])) {(do (swap! log conj :k1) :a) (do (swap! log conj :v1) 1) (do (swap! log conj :k2) :b) (do (swap! log conj :v2) 2)} (deref log))"]
["source order with a nil value (phm form)" "[1 2 3]"
"(do (def log (atom [])) {:a (do (swap! log conj 1) nil) :b (swap! log conj 2) :c (swap! log conj 3)} (deref log))"]
["source order through syntax-quote" "[1 2]"
"(do (def log (atom [])) (defmacro m-p3c [] `{:a ~(list 'swap! 'log 'conj 1) :b ~(list 'swap! 'log 'conj 2)}) (m-p3c) (deref log))"]
["count" "3" "(count {:a 1 :b 2 :c 3})"]
["equality with phm" "true" "(= {:a 1 :b 2} (assoc {:a 1} :b 2))"]
["keys work after assoc" "2" "(:b (assoc {:a 1 :b 2} :c 3))"]
@ -55,3 +61,14 @@
["PI" "true" "(< 3.14 clojure.math/PI 3.15)"]
["require + alias" "5" "(do (require '[clojure.math :as m]) (long (m/hypot 3 4)))"]
["as a value" "[1 2]" "(mapv (comp long clojure.math/sqrt) [1 4])"])
# jolt-507: a jolt local in janet CALL-HEAD position must not resolve as a
# janet core macro of the same name (repeat/seq/loop are janet macros). The
# emitter rebinds local callees to reserved _fp$ symbols.
(defspec "calls / locals named like janet macros"
["local fn named repeat" "[1 1]" "(let [repeat (fn [x] [x x])] (repeat 1))"]
["local fn named seq" ":end" "((fn seq [n] (if (pos? n) (seq (dec n)) :end)) 2)"]
["local fn named loop2" "[2 1]" "(let [with (fn [a b] [b a])] (with 1 2))"]
["overlay repeat self-call (regression)" "(quote (0 0 0))" "(take 3 (repeat 0))"]
["closure param called" "42" "((fn [f] (f 41)) inc)"]
["param holding a keyword (IFn leftover)" "1" "((fn [f] (f {:a 1})) :a)"])