Connect the E3.1 buffer to codegen. Push sites: `raise` (always escapes — push before cleanup) and `try`'s propagation branch (the failure that escapes to the caller). Clear sites: `catch` handler entry (via runCatchBody, error path only), the `or value` terminator's failure branch, and a destructure that binds a failable's error slot — so an absorbed failure leaves no residue. Helpers in lower.zig: emitTracePush / emitTraceClear (call getTraceFids, no-op when traces are off), tracesEnabled (opt_level == .none/.less — `sx run` defaults to -O0, so on in dev; .default/.aggressive are release → off, zero overhead), and placeholderTraceFrame (a nonzero u64 until DWARF/E3.0 supplies real PCs and E3.3 resolves them). Verified end-to-end via a #foreign sx_trace_len probe: catch/or/multi-slot- destructure drive len back to 0; release (--opt default) emits no push/clear at all (debug showed a residual where release showed 0). examples/241-error-trace-buffer.sx is a focused regression (white-box: reads sx_trace_len directly, pending E3.3's public trace.print_current). KNOWN GAP (documented, deferred to the E1.8 flow-check binding-site work): a single-binding capture of a PURE failable (`er := pure_failable()`, not a comma destructure) goes through lowerVarDecl, not lowerDestructureDecl, so it doesn't clear — the trace over-retains until the next absorbing site. Harmless today (nothing reads the buffer at function exit yet) but wrong per spec. Gates: zig build, zig build test, bash tests/run_examples.sh (278 passed; lone failure is the user's uncommitted 213-canonical-map pack WIP).
39 lines
1.4 KiB
Plaintext
39 lines
1.4 KiB
Plaintext
// Error return-trace buffer push/clear wiring (ERR step E3.2). A `raise` and a
|
|
// propagating `try` each push a frame; an absorbing site (`catch`, `or value`,
|
|
// a destructure that binds the error) clears the buffer. In debug builds
|
|
// (`sx run` defaults to -O0) these calls are emitted; in release they're
|
|
// skipped entirely (zero overhead). Until E3.3 ships `trace.print_current`,
|
|
// this example observes the buffer directly via the runtime's `sx_trace_len`
|
|
// (linked in for the JIT) — a white-box probe, not the eventual public API.
|
|
|
|
#import "modules/std.sx";
|
|
|
|
// Internal runtime symbol (library/vendors/sx_trace_runtime/sx_trace.c).
|
|
sx_trace_len :: () -> u32 #foreign;
|
|
|
|
E :: error { Bad }
|
|
|
|
fail :: (n: s32) -> !E {
|
|
if n < 0 { raise error.Bad; } // pushes a frame
|
|
return;
|
|
}
|
|
|
|
propagate :: (n: s32) -> !E {
|
|
try fail(n); // on failure: pushes a frame, propagates
|
|
return;
|
|
}
|
|
|
|
main :: () -> s32 {
|
|
// `catch` absorbs the failure → buffer cleared before the handler runs.
|
|
propagate(-1) catch e {
|
|
// The pushes from `raise` + `try` were cleared on catch entry.
|
|
print("in catch: len={}\n", sx_trace_len()); // 0
|
|
};
|
|
print("after catch: len={}\n", sx_trace_len()); // 0
|
|
|
|
// A success leaves the buffer empty (nothing pushed).
|
|
propagate(1) catch e { };
|
|
print("after success: len={}\n", sx_trace_len()); // 0
|
|
return 0;
|
|
}
|