A set literal reaches a macro as a set value

#{...} reads as the tagged set-form for the analyzer, but a macro saw that
map instead of a set (set? false / map? true, unlike a vector). hc-expand-1
now converts a set-form argument to a real set before calling the expander, so
(set? arg)/conj/seq work — hiccup's compiler introspects a literal set this way
(str (html #{"<>"}) was empty, now #{&quot;&lt;&gt;&quot;}). Elements stay as
read; a deeply-nested set literal inside another form is left for the analyzer.
hiccup 382->383. Jolt-side unit guards (macro def+use in one form isn't
JVM-portable).
This commit is contained in:
Yogthos 2026-06-26 22:42:19 -04:00
parent bd645a68d6
commit 5cd8d15ae7
2 changed files with 16 additions and 1 deletions

View file

@ -203,10 +203,22 @@
dst)) dst))
dst)) dst))
;; A set literal reads as the tagged set-form {:jolt/type :jolt/set :value [...]}
;; for the analyzer, but a macro must see a real set value (Clojure parity, so
;; (set? arg) / seq / conj work — hiccup's compiler does this). Convert a set-form
;; argument to a set; elements stay as read (a deeply-nested set literal inside
;; another form is rarer and left for the analyzer).
(define (hc-macro-arg x)
(if (rdr-set-form? x)
(let ((items (jolt-get x rdr-kw-value)))
(let loop ((i 0) (s empty-pset))
(if (fx>=? i (pvec-count items)) s
(loop (fx+ i 1) (pset-conj s (pvec-nth-d items i jolt-nil))))))
x))
(define (hc-expand-1 ctx form) (define (hc-expand-1 ctx form)
(let* ((items (seq->list form)) (let* ((items (seq->list form))
(head (car items)) (head (car items))
(args (cdr items)) (args (map hc-macro-arg (cdr items)))
(expander (var-cell-root (hc-resolve-cell ctx head)))) (expander (var-cell-root (hc-resolve-cell ctx head))))
(hc-propagate-pos form (apply jolt-invoke expander args)))) (hc-propagate-pos form (apply jolt-invoke expander args))))

View file

@ -567,4 +567,7 @@
{:suite "deftype-map" :expr "(do (deftype Op [s]) [(map? (->Op 1)) (record? (->Op 1)) (coll? (->Op 1))])" :expected "[false false false]"} {:suite "deftype-map" :expr "(do (deftype Op [s]) [(map? (->Op 1)) (record? (->Op 1)) (coll? (->Op 1))])" :expected "[false false false]"}
{:suite "deftype-map" :expr "(do (deftype Lc [xs] clojure.lang.Counted (count [_] (count xs)) clojure.lang.Seqable (seq [_] (seq xs))) [(coll? (->Lc [1 2])) (count (->Lc [1 2])) (vec (seq (->Lc [1 2])))])" :expected "[true 2 [1 2]]"} {:suite "deftype-map" :expr "(do (deftype Lc [xs] clojure.lang.Counted (count [_] (count xs)) clojure.lang.Seqable (seq [_] (seq xs))) [(coll? (->Lc [1 2])) (count (->Lc [1 2])) (vec (seq (->Lc [1 2])))])" :expected "[true 2 [1 2]]"}
{:suite "deftype-map" :expr "(do (defrecord Dr [a b]) [(map? (->Dr 1 2)) (record? (->Dr 1 2)) (coll? (->Dr 1 2))])" :expected "[true true true]"} {:suite "deftype-map" :expr "(do (defrecord Dr [a b]) [(map? (->Dr 1 2)) (record? (->Dr 1 2)) (coll? (->Dr 1 2))])" :expected "[true true true]"}
{:suite "macro-args" :expr "(do (defmacro sm [x] [(set? x) (map? x)]) (sm #{1 2}))" :expected "[true false]"}
{:suite "macro-args" :expr "(do (defmacro ws [x] (conj x :z)) (= #{1 :z} (ws #{1})))" :expected "true"}
{:suite "macro-args" :expr "(do (defmacro vm [x] (vector? x)) (vm [1 2]))" :expected "true"}
] ]