ERR/E3.0 (slice 1): thread source spans into IR instructions

Foundation for DWARF line-info (E3.0). The `Inst.span` field existed but was
never populated — `emit()` always passed the empty `{0,0}` default, so every
instruction had no source location (the lone reader, the interp's comptime
bail-offset, was always 0).

- Builder gains a `current_span`; `emit`/`emitVoid` stamp it onto each
  instruction.
- `lowerExpr` / `lowerStmt` set `current_span` from the AST node's span on
  entry and restore it on exit (save/restore), so a parent's later emits keep
  the parent's span after a child lowers; the empty default is skipped so
  synthetic nodes don't reset a meaningful enclosing span.

Behavior-neutral: codegen never reads spans, and the only consumer (the interp
bail-offset) merely gains real offsets. 290 examples pass unchanged, no `.ir`
snapshot drift. New unit test asserts an emitted `add` carries its `a + b` span.

Next (slice 2): bind `llvm-c/DebugInfo.h`, emit DICompileUnit / DISubprogram /
DIFile / DILocation from these spans, gate on debug/trace mode.
This commit is contained in:
agra
2026-06-01 12:52:14 +03:00
parent d67fb7b9b3
commit b44a5d05ef
3 changed files with 81 additions and 2 deletions

View File

@@ -1544,6 +1544,11 @@ pub const Lowering = struct {
}
fn lowerStmt(self: *Lowering, node: *const Node) void {
// Stamp this statement's span onto its instructions (ERR E3.0); see
// `lowerExpr`.
const saved_span = self.builder.current_span;
defer self.builder.current_span = saved_span;
if (node.span.start != 0 or node.span.end != 0) self.builder.current_span = .{ .start = node.span.start, .end = node.span.end };
switch (node.data) {
.var_decl => |vd| self.lowerVarDecl(&vd),
.const_decl => |cd| self.lowerConstDecl(&cd),
@@ -2253,6 +2258,14 @@ pub const Lowering = struct {
// ── Expression lowering ─────────────────────────────────────────
fn lowerExpr(self: *Lowering, node: *const Node) Ref {
// Stamp this node's source span onto the instructions it emits (ERR
// E3.0 — feeds DWARF line-info + comptime frame resolution). Save/
// restore so a parent's later emits keep the parent's span after a
// child lowers. Skip the empty default so synthetic nodes don't reset
// a meaningful enclosing span to offset 0.
const saved_span = self.builder.current_span;
defer self.builder.current_span = saved_span;
if (node.span.start != 0 or node.span.end != 0) self.builder.current_span = .{ .start = node.span.start, .end = node.span.end };
return switch (node.data) {
// Bare `$<pack>` in expression position → an `[]Type` slice
// value where each element is a `const_type(arg_types[i])`.