Make clojure.spec.alpha load and run

Four general gaps, shaken out by loading clojure.spec.alpha:

- Special forms were shadowable by a same-named macro. analyze-list
  macroexpanded before checking special forms, so a ns that redefs def/and/or
  (spec excludes them via :refer-clojure :exclude) made a bare def resolve to
  the macro instead of the special form, breaking every defn after. Now a head
  in the special-form set is never macroexpanded, matching the reference
  macroexpand1 isSpecial check.

- reify dropped all but the last arity of a multi-arity protocol method (spec
  reifies (specize* [s]) and (specize* [s _])). The macro keyed methods by name
  and overwrote; now it groups arities into one multi-arity fn.

- reify instances did not implement IObj: with-meta threw and (instance?
  clojure.lang.IObj r) was false. Every Clojure reify carries metadata. with-meta
  now copies the reify to a fresh identity (shared method table) and keys its
  meta; instance? IObj/IMeta is true for any reify. This was the registry bug —
  spec's with-name returned nil for specs, so get-spec missed.

- (set! (. Class field) val) was rejected. spec toggles
  clojure.lang.RT/checkSpecAsserts this way; the analyzer now lowers it to a
  jolt.host/set-static-field! call over a mutable-statics table, and a plain
  Class/field read consults that table.

Also: .name/.getName on a Namespace and .ns/.sym on a Var (spec's ns-qualify /
->sym). analyzer + reify are seed sources (re-minted). spec.alpha now does
valid?/conform/cat/keys/explain-str/check-asserts. tick.alpha.interval-test still
needs time-literals data readers (separate).
This commit is contained in:
Yogthos 2026-06-24 19:46:22 -04:00
parent f417516148
commit 7a343351d6
11 changed files with 598 additions and 532 deletions

View file

@ -448,15 +448,22 @@
;; a reify can implement SEVERAL protocols; collect them all (each bare symbol
;; switches the current protocol, like extend-type) and pass every protocol name
;; to make-reified so (instance? Proto r)/satisfies? recognise all of them.
(loop [items (seq forms) protos [] methods {}]
;; Several bodies for the same method name are distinct arities (clojure.spec
;; reifies (specize* [s]) and (specize* [s _])): group them into one multi-arity
;; fn so dispatch picks the clause by arg count.
(loop [items (seq forms) protos [] methods {} order []]
(if (empty? items)
`(make-reified ~methods ~@(vec (map name protos)))
`(make-reified
~(reduce (fn [m k] (assoc m k `(fn ~@(get methods k)))) {} order)
~@(vec (map name protos)))
(let [x (first items)]
(if (symbol? x)
(recur (rest items) (conj protos x) methods)
(recur (rest items) protos
(assoc methods (keyword (name (first x)))
`(fn ~(nth x 1) ~@(drop 2 x)))))))))
(recur (rest items) (conj protos x) methods order)
(let [k (keyword (name (first x)))
clause `(~(nth x 1) ~@(drop 2 x))]
(recur (rest items) protos
(assoc methods k (conj (get methods k []) clause))
(if (contains? methods k) order (conj order k)))))))))
(defmacro defrecord [name-sym fields & body]
(let [tn (name name-sym)