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:
@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
|
||||
const types = @import("types.zig");
|
||||
const inst_mod = @import("inst.zig");
|
||||
const mod_mod = @import("module.zig");
|
||||
const errors = @import("../errors.zig");
|
||||
|
||||
const TypeId = types.TypeId;
|
||||
const TypeTable = types.TypeTable;
|
||||
@@ -148,6 +149,12 @@ pub const Interpreter = struct {
|
||||
/// tracked — foreign calls return before the frame is pushed.
|
||||
call_chain: std.ArrayList(FuncId) = .empty,
|
||||
|
||||
/// File → source text (the diagnostics' import_sources). Set by the host
|
||||
/// where available so `.trace_resolve` can turn a `(func_id, span.start)`
|
||||
/// frame into `file:line:col` at comptime (ERR E3.0 slice 3b). Null → the
|
||||
/// resolver degrades to line/col 1:1.
|
||||
source_map: ?*const std.StringHashMap([:0]const u8) = null,
|
||||
|
||||
// Heap: dynamically allocated memory blocks
|
||||
heap: std.ArrayList([]u8),
|
||||
|
||||
@@ -203,6 +210,12 @@ pub const Interpreter = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Provide the file→source map so `.trace_resolve` can compute file:line:col
|
||||
/// for comptime trace frames. Optional — absent in unit tests.
|
||||
pub fn setSourceMap(self: *Interpreter, sm: *const std.StringHashMap([:0]const u8)) void {
|
||||
self.source_map = sm;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Interpreter) void {
|
||||
// Free all heap allocations
|
||||
for (self.heap.items) |block| {
|
||||
@@ -652,6 +665,32 @@ pub const Interpreter = struct {
|
||||
const packed_frame: u64 = (fid << 32) | @as(u64, instruction.span.start);
|
||||
return .{ .value = .{ .int = @bitCast(packed_frame) } };
|
||||
},
|
||||
.trace_resolve => |u| {
|
||||
// Unpack the comptime frame `(func_id << 32 | span.start)` and
|
||||
// resolve it to a `Frame { file, line, col, func }` aggregate.
|
||||
const raw: u64 = @bitCast(frame.getRef(u.operand).asInt() orelse 0);
|
||||
const fid: u32 = @intCast(raw >> 32);
|
||||
const offset: u32 = @truncate(raw);
|
||||
const func = self.module.getFunction(FuncId.fromIndex(fid));
|
||||
const func_name = self.module.types.getString(func.name);
|
||||
const file_full = func.source_file orelse "";
|
||||
const file = std.fs.path.basename(file_full);
|
||||
var line: i64 = 1;
|
||||
var col: i64 = 1;
|
||||
if (self.source_map) |sm| {
|
||||
if (sm.get(file_full)) |src| {
|
||||
const loc = errors.SourceLoc.compute(src, offset);
|
||||
line = @intCast(loc.line);
|
||||
col = @intCast(loc.col);
|
||||
}
|
||||
}
|
||||
const fields = self.alloc.alloc(Value, 4) catch return .{ .value = .undef };
|
||||
fields[0] = .{ .string = file };
|
||||
fields[1] = .{ .int = line };
|
||||
fields[2] = .{ .int = col };
|
||||
fields[3] = .{ .string = func_name };
|
||||
return .{ .value = .{ .aggregate = fields } };
|
||||
},
|
||||
.const_type => |tid| return .{ .value = .{ .type_tag = tid } },
|
||||
|
||||
// ── Arithmetic ──────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user