Reader attaches collection metadata to data, not a with-meta form

The reader lowered ^meta on a vector/map/set literal to a runtime
(with-meta form meta) list, so read-string/edn of data with metadata
returned the form and lost the metadata. Attach it to the value instead,
as Clojure does; the analyzer re-emits (with-meta coll meta) for a
meta-carrying collection literal in code, so a literal still carries its
metadata at runtime and ^Type/^long arglist hints (consumed by
analyze-arity directly) are unaffected.

Also: pr honors *print-meta*, and clojure.walk/clojure.edn re-attach
metadata to the collections they rebuild (matches Clojure; a
metadata-driven config lib like aero relies on it).
This commit is contained in:
Yogthos 2026-06-24 12:04:36 -04:00
parent 091d2c4b62
commit 473d1002b7
8 changed files with 763 additions and 716 deletions

View file

@ -26,7 +26,7 @@
form-bigdec? form-bigdec-source
form-ns-value? form-ns-value-name
form-macro? form-expand-1 resolve-global
form-sym-meta host-intern! form-syntax-quote-lower
form-sym-meta form-coll-meta host-intern! form-syntax-quote-lower
record-type? record-ctor-key form-position late-bind?
resolve-class-hint]]))
@ -590,17 +590,31 @@
p (form-position form)]
(if p (assoc n :pos p) n)))))))
;; A vector/map/set literal carrying reader metadata (^:foo {…}, ^{:tag :int} [1])
;; keeps it as a runtime value: wrap the collection node in (with-meta coll meta).
;; The metadata is itself a form (its values may be expressions, ^{:a (f)}), so
;; analyze it. nil meta passes the node through. Arglist vectors never reach here —
;; analyze-arity reads their items directly — so a ^Type [args] hint is not wrapped.
(defn- with-coll-meta [ctx form env node]
(let [m (form-coll-meta form)]
(if (nil? m)
node
(invoke (var-ref "clojure.core" "with-meta") [node (analyze ctx m env)]))))
(defn analyze
([ctx form] (analyze ctx form (empty-env)))
([ctx form env]
(cond
(form-literal? form) (const form)
(form-sym? form) (analyze-symbol ctx form env)
(form-vec? form) (vector-node (mapv #(analyze ctx % env) (form-vec-items form)))
(form-map? form) (map-node (mapv (fn [p] [(analyze ctx (first p) env)
(analyze ctx (second p) env)])
(form-map-pairs form)))
(form-set? form) (set-node (mapv #(analyze ctx % env) (form-set-items form)))
(form-vec? form) (with-coll-meta ctx form env
(vector-node (mapv #(analyze ctx % env) (form-vec-items form))))
(form-map? form) (with-coll-meta ctx form env
(map-node (mapv (fn [p] [(analyze ctx (first p) env)
(analyze ctx (second p) env)])
(form-map-pairs form))))
(form-set? form) (with-coll-meta ctx form env
(set-node (mapv #(analyze ctx % env) (form-set-items form))))
(form-list? form) (analyze-list ctx form env)
;; regex literal #"…" -> a :regex IR node (leaf). The Chez back end emits a
;; jolt-regex value over the vendored irregex.