ERR/E3.0 (slice 3b): comptime trace resolution

#run failures now print the same `func at file:line:col` trace as
runtime, resolved in-process via the interpreter's IR/source tables.

- Read-side context-split op `.trace_resolve` (mirror of .trace_frame),
  lowered from a name-recognized `__trace_resolve_frame(u64) -> Frame`.
- emit_llvm: inttoptr the operand to *Frame + load (the value
  .trace_frame stamped in).
- interp: unpack (func_id << 32 | span.start); resolve func/file from
  module.functions and line/col via SourceLoc.compute over a new
  source_map (setSourceMap wired at every production interp site).
- trace.sx: frame_at -> u64; to_string routes each frame through
  __trace_resolve_frame, so one source works in both machines.

Compiled path behavior unchanged (243/244/247 identical; it now loads
via the op). New examples/253-comptime-trace.sx exercises the comptime
path. Gates: zig build, zig build test, run_examples.sh -> 291 passed.
This commit is contained in:
agra
2026-06-01 15:33:50 +03:00
parent 11f6377d9c
commit b5241243e6
11 changed files with 120 additions and 5 deletions

View File

@@ -8554,6 +8554,7 @@ pub const Lowering = struct {
var interp = interp_mod.Interpreter.init(self.module, self.alloc);
defer interp.deinit();
if (self.diagnostics) |d| if (d.import_sources) |sm| interp.setSourceMap(sm);
const result = interp.call(ct_func_id, &.{}) catch return null;
@@ -10345,6 +10346,18 @@ pub const Lowering = struct {
// chain at comptime, no-op in compiled code (ERR E4.1).
return self.builder.emit(.{ .interp_print_frames = {} }, .void);
}
if (std.mem.eql(u8, name, "__trace_resolve_frame")) {
// Backs `trace.sx`'s formatter: a raw trace-buffer u64 → a `Frame`.
// Compiled code reinterprets the operand as `*Frame` and loads it;
// the interp unpacks (func_id, span.start) and resolves (ERR E3.0
// slice 3b). Result type is the `Frame` struct from trace.sx.
const frame_ty = self.module.types.findByName(self.module.types.internString("Frame")) orelse {
if (self.diagnostics) |d| d.addFmt(.err, null, "`__trace_resolve_frame` needs `Frame` (from trace.sx) in scope", .{});
return self.builder.constInt(0, .void);
};
const arg = self.lowerExpr(c.args[0]);
return self.builder.emit(.{ .trace_resolve = .{ .operand = arg } }, frame_ty);
}
if (std.mem.eql(u8, name, "error_tag_name")) {
// error_tag_name(e) → look the error-set value's runtime tag id up
// in the always-linked tag-name table. The value IS its u32 tag id.
@@ -13981,6 +13994,8 @@ pub const Lowering = struct {
if (std.mem.eql(u8, bare_name, "error_tag_name")) return .string;
if (std.mem.eql(u8, bare_name, "is_comptime")) return .bool;
if (std.mem.eql(u8, bare_name, "__interp_print_frames")) return .void;
if (std.mem.eql(u8, bare_name, "__trace_resolve_frame"))
return self.module.types.findByName(self.module.types.internString("Frame")) orelse .unresolved;
if (std.mem.eql(u8, bare_name, "is_flags")) return .bool;
if (std.mem.eql(u8, bare_name, "type_of")) return .any;
if (std.mem.eql(u8, bare_name, "field_value")) return .any;