Recover TCO-elided frames in uncaught-error stack traces
On the eval path nothing registers a source map, so jolt-backtrace-string dropped every walkable frame and printed no trace at all. Keep any named, non-plumbing continuation frame (rendered as a bare name when unmapped) so a runtime error shows the surviving non-tail spine — "print what is available". Add an opt-in tail-frame history behind JOLT_TRACE for the frames TCO erases. Each compiled fn records itself on entry into a bounded ring-of-rings, MIT Scheme's "history" shape: the outer ring holds one rib per non-tail subproblem, each rib a small inner ring of the tail-calls made at that level. A tight tail loop churns one rib instead of flushing the spine, so the non-tail caller context survives and total space stays bounded. The reporter prefers this history over the continuation when it's present, and resets it per top-level form so an error's trace isn't padded with earlier REPL frames. The emitter marks a tail call with (jolt-trace-mark! #t) so the runtime routes the callee into the current rib vs a fresh one; a *tail?* dynamic var tracks tail position (cleared by default, passed through if/do/let/loop/fn-body). It's all gated on trace-frames?, which compile-eval turns on for JOLT_TRACE and emit-image/`jolt build` force off — so non-trace emitted output is byte-identical (prelude unchanged, seed re-minted), and a built binary carries no per-call cost.
This commit is contained in:
parent
773e647b4a
commit
a3e2365217
8 changed files with 511 additions and 170 deletions
|
|
@ -182,6 +182,20 @@ a root, transitively.
|
|||
- Source only; compiled `.class` files in a git dep are ignored.
|
||||
- git `:git/sha` must be a full SHA (`git fetch` can't resolve a short one).
|
||||
|
||||
## Stack traces
|
||||
|
||||
An uncaught error prints the message, the top-level source location, and — when
|
||||
frames are available — a `trace:` backtrace. In an AOT `jolt build --direct-link`
|
||||
binary the frames map to `ns/name (file:line)`; on the runtime eval path they are
|
||||
the surviving fn names. Tail-call optimization erases tail-called frames, so the
|
||||
default trace shows only the non-tail spine.
|
||||
|
||||
Set `JOLT_TRACE=1` to opt into a fuller **tail-frame history**. Each compiled fn
|
||||
then records itself on entry into a bounded ring-of-rings buffer, so the trace
|
||||
recovers TCO-elided frames (including the immediate error site) while a tight tail
|
||||
loop stays bounded and its non-tail caller context is preserved. It costs a small
|
||||
per-call overhead, so it is off by default and never emitted into a built binary.
|
||||
|
||||
## Conformance
|
||||
|
||||
The known-working libraries (see [libraries.md](libraries.md)) and the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue