records use declared-shape layout with fast field access by default (jolt-t34)
Records (defrecord/deftype) are now shape-recs in a direct-linking unit by
default — no JOLT_SHAPE flag. A record's shape is DECLARED, so the inference
proves field reads by a lookup, not fragile shape inference, and they bare-index.
Result: ~1.4x faster than the :jolt/deftype table form on a record-heavy loop
(3.9s vs 5.5s), driven by cheaper construction + proven bare-index reads.
Two gates now:
- :shapes? — shape-recs active; records use declared-shape layout + bare
index reads. On with direct-linking (where the inference runs).
- :map-shapes? — also shape generic const-key maps. Opt-in (JOLT_SHAPE), because
shaping maps net-loses on unproven reads (measured). Records win.
- call-ret-type types a record ctor (->Name) as a struct of its declared shape,
fed from a ctx-env registry populated at deftype; field reads on the result
bare-index. (set-record-shapes!/set-map-shapes! wired through infer-unit!.)
- sidx reads the field's position from the :shape vector AS-IS (declared order
for records, str-sorted for map literals) — no re-sort — so any field order
bare-indexes correctly. The map :map case only sets :shape under :map-shapes?.
- record-shape-for interns the descriptor per (type, fields), not per type: a
record redefined with different fields now gets a fresh descriptor instead of a
stale one (fixes redef descriptor staleness; old instances stay valid).
Adds record-declared-shape-test (declared-order reads, incl. non-alphabetical
fields, through fn boundaries + protocol method bodies). Known pre-existing edge
case filed as jolt-wf4 (direct (:f (->R …)) read returns nil after a record is
redefined with different fields; let-bound read works; repros without shapes).
This commit is contained in:
parent
4e7b8f792f
commit
f5a5b25d59
7 changed files with 111 additions and 26 deletions
|
|
@ -875,6 +875,15 @@
|
|||
;; provably wrong and the CALL is reported. Module state, like rtenv-box: a def
|
||||
;; must precede its call (the same closed-world ordering RFC 0005 assumes).
|
||||
(def ^:private user-sig-box (atom {})) ;; "ns/name" -> {:params [..] :body ir}
|
||||
;; jolt-t34: a record constructor's return shape. "ns/->Name" -> [field-kw ...]
|
||||
;; in DECLARED order (the runtime lays records out in declared field order, so
|
||||
;; the back end bare-indexes by that order). A call (->Point a b) types as a
|
||||
;; struct of this shape, so field reads on the result bare-index — declared
|
||||
;; shapes are clean fuel: a lookup, not fragile inference.
|
||||
(def ^:private record-shapes-box (atom {}))
|
||||
;; jolt-t34: whether to shape generic const-key MAP literals (opt-in, JOLT_SHAPE).
|
||||
;; Records are shaped regardless; maps only when this is on.
|
||||
(def ^:private map-shapes-box (atom false))
|
||||
(def ^:private checking-box (atom #{})) ;; keys mid-recheck — cycle guard
|
||||
(def ^:private strict-box (atom false)) ;; report against user-fn domains?
|
||||
;; When true, `infer` emits success-type diagnostics as it types (jolt audit).
|
||||
|
|
@ -897,12 +906,18 @@
|
|||
(let [op (get fnode :op)]
|
||||
(cond
|
||||
;; a user fn whose return type the fixpoint has estimated
|
||||
(= op :var) (let [r (get @rtenv-box (var-key fnode))]
|
||||
(if r r (let [nm (and (= "clojure.core" (get fnode :ns)) (get fnode :name))]
|
||||
(cond (nil? nm) :any
|
||||
(contains? num-ret-fns nm) :num
|
||||
(contains? vector-ret-fns nm) (mk-vec :any)
|
||||
:else :any))))
|
||||
(= op :var) (let [rs (get @record-shapes-box (var-key fnode))]
|
||||
(if rs
|
||||
;; record ctor -> struct of declared shape (jolt-t34); :shape
|
||||
;; is the DECLARED field order, which the back end indexes by
|
||||
(assoc (mk-struct (reduce (fn [m k] (assoc m k :any)) {} rs))
|
||||
:shape (vec rs))
|
||||
(let [r (get @rtenv-box (var-key fnode))]
|
||||
(if r r (let [nm (and (= "clojure.core" (get fnode :ns)) (get fnode :name))]
|
||||
(cond (nil? nm) :any
|
||||
(contains? num-ret-fns nm) :num
|
||||
(contains? vector-ret-fns nm) (mk-vec :any)
|
||||
:else :any))))))
|
||||
(= op :host) (let [nm (get fnode :name)]
|
||||
(cond (contains? num-ret-fns nm) :num
|
||||
(contains? vector-ret-fns nm) (mk-vec :any)
|
||||
|
|
@ -976,7 +991,7 @@
|
|||
(cap (mk-struct (reduce (fn [m r] (assoc m (nth r 3) (nth r 2))) {} res)) type-depth))
|
||||
;; a literal is a COMPLETE shape: carry its sorted key vector so the
|
||||
;; back end can lay it out and bare-index lookups (jolt-t34)
|
||||
shp (when (and base (struct-type? base)) (shape-order (keys (sfields base))))
|
||||
shp (when (and @map-shapes-box base (struct-type? base)) (shape-order (keys (sfields base))))
|
||||
t (if base (if shp (assoc base :shape shp) base) :any)
|
||||
node' (assoc node :pairs (mapv (fn [r] [(nth r 0) (nth r 1)]) res))]
|
||||
[t (if shp (assoc node' :shape shp) node')])
|
||||
|
|
@ -1312,6 +1327,11 @@
|
|||
type call results during the fixpoint."
|
||||
[m] (reset! rtenv-box m))
|
||||
|
||||
;; jolt-t34: install record-ctor shapes ("ns/->Name" -> [field-kw ...]) and the
|
||||
;; map-shaping flag (opt-in JOLT_SHAPE), both read by infer.
|
||||
(defn set-record-shapes! [m] (reset! record-shapes-box (or m {})))
|
||||
(defn set-map-shapes! [b] (reset! map-shapes-box (boolean b)))
|
||||
|
||||
(defn set-vtypes!
|
||||
"Install var VALUE types (a map \"ns/name\" -> type): fn vars are :truthy
|
||||
(non-nil), def vars carry their inferred init type (jolt-d6u)."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue