Support mutable deftype fields under shapes (jolt-c3q) (#125)

A deftype field tagged ^:unsynchronized-mutable / ^:volatile-mutable is set!-able,
but under direct-link immutable records are shape-rec tuples, so set! errored
("Can't set! field on non-deftype: tuple").

A deftype with any mutable field now opts out of the shape-rec layout and uses
the existing :jolt/deftype table form regardless of :shapes? — set! already
mutates that form and field reads route through the tagged-table path. Such a
type is also not registered as a shape, so the inference never emits a bare-index
read against the table. Immutable deftypes/records keep the fast shape-rec.

deftype extracts per-field mutability from the field metadata and passes it to
make-deftype-ctor, which picks the representation at ctor-build time.

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

View file

@ -295,6 +295,15 @@
(and mt (:num mt)) "num"
:else nil)))
fields)
;; per-field MUTABILITY (jolt-c3q): ^:unsynchronized-mutable / ^:volatile-
;; mutable marks a field set!-able. A type with any mutable field opts out
;; of the immutable shape-rec layout and uses the mutable table form, so
;; set! can mutate it (the ctor reads this vector). Spliced as a literal.
field-muts (map (fn [f] (let [mt (meta f)]
(if (and mt (or (:unsynchronized-mutable mt)
(:volatile-mutable mt)))
true false)))
fields)
impl (fn [proto specs]
`(extend-type ~tname ~proto
~@(map (fn [spec]
@ -304,7 +313,7 @@
`(~(first spec) ~argv (let [~@binds] ~@(drop 2 spec)))))
specs)))]
`(do
(def ~tname (make-deftype-ctor (quote ~tname) [~@field-kws] [~@field-tags]))
(def ~tname (make-deftype-ctor (quote ~tname) [~@field-kws] [~@field-tags] [~@field-muts]))
(def ~arrow ~tname)
~@(map (fn [g] (impl (first g) (rest g))) (group-by-head body))
~tname)))