Propagate source position through macroexpansion

hc-expand-1 now carries the macro call form's :line/:column onto the top of a
list expansion that has none of its own (merged under any meta the macro set),
so errors and stack traces in macro-generated code point at the call site —
Clojure parity. The analyze recursion re-expands inner macros, so each level's
top form picks it up, matching the reference compiler. (meta (macroexpand-1
'(when x y))) now reports the call-site line.

A direct-link fn defined through a user macro (build-app's defguarded) registers
with a real line, so build-smoke's trace assertion covers macro-defined fns.

Runtime .ss (host-contract.ss) — no re-mint; selfhost holds.

Phase 3's optional items are deferred: :line-in-ex-data has no clean consumer
(it would pollute ex-data, break = and printing, and positions already surface
via the trace + top-level location), and Chez source-object emission is a large
backend change the jv$-name registry already sidesteps.
This commit is contained in:
Yogthos 2026-06-25 21:56:08 -04:00
parent 57bab5d409
commit 8fd5cf9f68
3 changed files with 30 additions and 4 deletions

View file

@ -5,9 +5,13 @@
(str/upper-case (str s "!")))
;; A two-deep non-tail call chain that throws — exercises native stack traces in a
;; direct-link build (build-smoke runs -main with a --boom sentinel arg).
(defn deep-boom [x]
(assert (number? x) "needs a number")
;; direct-link build (build-smoke runs -main with a --boom sentinel arg). deep-boom
;; is defined through a USER macro: its source registration only gets a real line
;; if the reader position survives macroexpansion (so the trace frame maps).
(defmacro defguarded [name args & body]
`(defn ~name ~args (assert (number? ~(first args)) "needs a number") ~@body))
(defguarded deep-boom [x]
(* x 2))
(defn mid-boom [x]

View file

@ -339,6 +339,9 @@
{:suite "reader" :expr "(nil? (meta (read-string \"()\")))" :expected "true"}
{:suite "reader" :expr "(:foo (meta (read-string \"^:foo (x y)\")))" :expected "true"}
{:suite "reader" :expr "(:line (meta (read-string \"^:foo (x y)\")))" :expected "1"}
{:suite "reader" :expr "(:line (meta (macroexpand-1 (read-string \"(when x y)\"))))" :expected "1"}
{:suite "reader" :expr "(:line (meta (macroexpand-1 (read-string \"\\n\\n(when x y)\"))))" :expected "3"}
{:suite "reader" :expr "(:column (meta (macroexpand-1 (read-string \"(when x y)\"))))" :expected "1"}
{:suite "reader" :expr "(= 42 (with-in-str \"42\" (read)))" :expected "true"}
{:suite "reader" :expr "(= (quote (+ 1 2)) (with-in-str \"(+ 1 2)\" (read)))" :expected "true"}
{:suite "reader" :expr "(with-in-str \"1 2\" [(read) (read)])" :expected "[1 2]"}