mutable deftype fields: (set! field val) in a method
deftype fields tagged ^:unsynchronized-mutable / ^:volatile-mutable can now be reassigned in place from a method, as on the JVM. A jrec stores fields as cons cells, so a new jolt-set-field! mutates the pair with set-cdr!. The deftype macro rewrites (set! mutable-field v) in a method body to (set! (.-field inst) v), and the analyzer compiles a (set! (.-field obj) v) target to jolt-set-field! — so both the rewritten symbol form and an explicit interop (set! (.-root this) v) go through one path. Field reads remain a snapshot at method entry, which is correct for the universal read-then-set pattern (a repeated set! of the same field in one call would read the entry value). Closes the set!-of-local SCI failures: SCI load 202 -> 205/218.
This commit is contained in:
parent
b9ab750983
commit
424ce75cf6
9 changed files with 416 additions and 371 deletions
|
|
@ -307,6 +307,21 @@
|
|||
(:volatile-mutable mt)))
|
||||
true false)))
|
||||
fields)
|
||||
;; mutable field symbols (^:unsynchronized-mutable / ^:volatile-mutable):
|
||||
;; (set! field v) in a method body lowers to (set! (.-field inst) v), the
|
||||
;; in-place field write the analyzer compiles to jolt-set-field! (jolt-c3q).
|
||||
mutable-syms (map first (filter second (map vector fields field-muts)))
|
||||
mutable? (fn [s] (boolean (some (fn [m] (= m s)) mutable-syms)))
|
||||
rewrite-set (fn rw [inst form]
|
||||
(cond
|
||||
(and (seq? form) (seq form) (symbol? (first form))
|
||||
(= "set!" (name (first form)))
|
||||
(symbol? (second form)) (mutable? (second form)))
|
||||
(list 'set! (list (symbol (str ".-" (name (second form)))) inst)
|
||||
(rw inst (nth form 2)))
|
||||
(seq? form) (map (fn [x] (rw inst x)) form)
|
||||
(vector? form) (mapv (fn [x] (rw inst x)) form)
|
||||
:else form))
|
||||
;; inline impls register for dispatch but are NOT extenders of the
|
||||
;; protocol (the JVM compiles them into the class) — register-inline-method,
|
||||
;; not extend-type.
|
||||
|
|
@ -315,9 +330,10 @@
|
|||
~@(map (fn [spec]
|
||||
(let [argv (nth spec 1)
|
||||
inst (first argv)
|
||||
binds (vec (mapcat (fn [f] [f `(get ~inst ~(keyword (name f)))]) fields))]
|
||||
binds (vec (mapcat (fn [f] [f `(get ~inst ~(keyword (name f)))]) fields))
|
||||
mbody (map (fn [bf] (rewrite-set inst bf)) (drop 2 spec))]
|
||||
`(register-inline-method ~(name tname) ~(name proto) ~(name (first spec))
|
||||
(fn ~argv (let [~@binds] ~@(drop 2 spec))))))
|
||||
(fn ~argv (let [~@binds] ~@mbody)))))
|
||||
specs)))]
|
||||
`(do
|
||||
(def ~tname (make-deftype-ctor (quote ~tname) [~@field-kws] [~@field-tags] [~@field-muts]))
|
||||
|
|
|
|||
|
|
@ -343,13 +343,24 @@
|
|||
(host-intern! ctx cur nm)
|
||||
{:op :defmacro :ns cur :name nm
|
||||
:fn (analyze ctx fn-form env)})
|
||||
"set!" (let [target (nth items 1)]
|
||||
(when-not (form-sym? target) (uncompilable "set! of a non-symbol target"))
|
||||
(when (local? env (form-sym-name target)) (uncompilable "set! of a local"))
|
||||
(let [r (resolve-global ctx target)]
|
||||
(when-not (= :var (:kind r)) (uncompilable "set! of a non-var"))
|
||||
{:op :set-var :the-var (the-var (:ns r) (:name r))
|
||||
:val (analyze ctx (nth items 2) env)}))
|
||||
"set!" (let [target (nth items 1)
|
||||
val-node (analyze ctx (nth items 2) env)
|
||||
ti (when (form-list? target) (vec (form-elements target)))
|
||||
thead (when (and ti (pos? (count ti)) (form-sym? (first ti)))
|
||||
(form-sym-name (first ti)))]
|
||||
(cond
|
||||
;; (set! (.-field obj) v): mutate a deftype instance field in place.
|
||||
;; A deftype method's (set! mutable-field v) lowers to this shape.
|
||||
(and thead (field-head? thead))
|
||||
{:op :set-field :obj (analyze ctx (nth ti 1) env)
|
||||
:field (subs thead 2) :val val-node}
|
||||
;; (set! *var* v): set the var's innermost binding, else its root.
|
||||
(form-sym? target)
|
||||
(do (when (local? env (form-sym-name target)) (uncompilable "set! of a local"))
|
||||
(let [r (resolve-global ctx target)]
|
||||
(when-not (= :var (:kind r)) (uncompilable "set! of a non-var"))
|
||||
{:op :set-var :the-var (the-var (:ns r) (:name r)) :val val-node}))
|
||||
:else (uncompilable "set! of an unsupported target")))
|
||||
(uncompilable (str "special form " op))))
|
||||
|
||||
;; Host interop method call (jolt-0kf5). `(.method target arg*)` — a head that
|
||||
|
|
|
|||
|
|
@ -392,6 +392,9 @@
|
|||
:the-var (str "(jolt-var " (chez-str-lit (:ns node)) " " (chez-str-lit (:name node)) ")")
|
||||
;; (set! *var* val) -> set the var's innermost binding (else root); returns val.
|
||||
:set-var (str "(jolt-var-set " (emit (:the-var node)) " " (emit (:val node)) ")")
|
||||
;; (set! (.-field obj) val) -> mutate the deftype instance field in place.
|
||||
:set-field (str "(jolt-set-field! " (emit (:obj node)) " (keyword #f "
|
||||
(chez-str-lit (:field node)) ") " (emit (:val node)) ")")
|
||||
;; a non-top-level defmacro -> def the expander fn + mark the var a macro at
|
||||
;; runtime (the spine does the same for top-level forms).
|
||||
:defmacro (str "(begin (def-var! " (chez-str-lit (:ns node)) " " (chez-str-lit (:name node)) " "
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@
|
|||
:ret (f (get node :ret)))
|
||||
(= op :throw) (assoc node :expr (f (get node :expr)))
|
||||
(= op :set-var) (assoc node :val (f (get node :val)))
|
||||
(= op :set-field) (assoc node :obj (f (get node :obj)) :val (f (get node :val)))
|
||||
(= op :defmacro) (assoc node :fn (f (get node :fn)))
|
||||
(= op :invoke) (assoc node :fn (f (get node :fn))
|
||||
:args (mapv f (get node :args)))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue