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:
parent
57bab5d409
commit
8fd5cf9f68
3 changed files with 30 additions and 4 deletions
|
|
@ -184,12 +184,31 @@
|
|||
;; of the list), and the analyzer re-analyzes the returned form.
|
||||
(define (hc-macro? ctx sym)
|
||||
(macro-var? (hc-resolve-cell ctx sym)))
|
||||
;; Clojure parity: a macro expansion inherits the call form's source position, so
|
||||
;; errors/traces in macro-generated code point at the macro call site. Carry it
|
||||
;; onto the top of a LIST expansion (code) that has none of its own — merged under
|
||||
;; any meta the macro set, leaving collection literals (runtime data) alone. The
|
||||
;; recursion through analyze re-expands inner macros, so each level's top form
|
||||
;; picks up the position the same way (as the reference compiler does).
|
||||
(define (hc-propagate-pos src dst)
|
||||
(if (and (cseq? dst) (cseq-list? dst))
|
||||
(let ((sp (hc-form-position src))
|
||||
(dm (jolt-meta dst)))
|
||||
(if (and (pmap? sp)
|
||||
(or (jolt-nil? dm) (jolt-nil? (jolt-get dm hc-kw-line))))
|
||||
(jolt-with-meta dst
|
||||
(if (pmap? dm)
|
||||
(pmap-fold sp (lambda (k v acc) (jolt-assoc1 acc k v)) dm)
|
||||
sp))
|
||||
dst))
|
||||
dst))
|
||||
|
||||
(define (hc-expand-1 ctx form)
|
||||
(let* ((items (seq->list form))
|
||||
(head (car items))
|
||||
(args (cdr items))
|
||||
(expander (var-cell-root (hc-resolve-cell ctx head))))
|
||||
(apply jolt-invoke expander args)))
|
||||
(hc-propagate-pos form (apply jolt-invoke expander args))))
|
||||
|
||||
;; Classify a global (non-local) symbol reference against the var registry:
|
||||
;; {:kind :var :ns NS :name NAME} — a defined var (compile ns / clojure.core)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]"}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue