refactor: new direction for error reporting

This commit is contained in:
Brett Broadhurst 2026-03-18 17:52:07 -06:00
parent 72b686d750
commit c940374f27
Failed to generate hash of commit
11 changed files with 758 additions and 582 deletions

View file

@ -4,16 +4,122 @@ const Writer = @import("print_ir.zig").Writer;
const assert = std.debug.assert;
const Ir = @This();
string_bytes: []u8,
/// List of IR instructions for the translation unit.
instructions: []Inst,
globals: []Global,
/// Interned string bytes. All strings are null-terminated.
/// Index 0 is reserved for the empty string.
string_bytes: []u8,
/// Ancillary data for instructions. The meaning of this data is determined by
/// the value of `Inst.Tag`. See `ExtraIndex` for the values of reserved indexes.
extra: []u32,
errors: []Ast.Error,
globals: []Global,
pub const ExtraIndex = enum(u32) {
/// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
/// payload at this index.
compile_errors,
_,
};
fn ExtraData(comptime T: type) type {
return struct {
data: T,
end: usize,
};
}
/// Extract a slice of `extra` into an `Inst` payload structure.
pub fn extraData(ir: Ir, comptime T: type, index: usize) ExtraData(T) {
const fields = @typeInfo(T).@"struct".fields;
var i: usize = index;
var result: T = undefined;
inline for (fields) |field| {
@field(result, field.name) = switch (field.type) {
u32 => ir.extra[i],
Inst.Index => @enumFromInt(ir.extra[i]),
Inst.Ref => @enumFromInt(ir.extra[i]),
NullTerminatedString => @enumFromInt(ir.extra[i]),
else => @compileError("bad field type"),
};
i += 1;
}
return .{ .data = result, .end = i };
}
pub const NullTerminatedString = enum(u32) {
empty,
_,
};
pub fn nullTerminatedString(ir: Ir, index: NullTerminatedString) [:0]const u8 {
const slice = ir.string_bytes[@intFromEnum(index)..];
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
}
pub fn bodySlice(ir: Ir, start: usize, len: usize) []Inst.Index {
return @ptrCast(ir.extra[start..][0..len]);
}
pub fn hasCompileErrors(ir: Ir) bool {
if (ir.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0) {
return true;
} else {
assert(ir.instructions.len != 0);
return false;
}
}
pub fn render(ir: Ir, writer: *std.Io.Writer) !void {
if (ir.instructions.len > 0) {
var w: Writer = .{ .code = ir };
try w.writeInst(writer, .file_inst);
return writer.flush();
}
}
pub fn dumpInfo(ir: Ir, writer: *std.Io.Writer) !void {
const bytes = ir.string_bytes;
var start: usize = 0;
while (start < bytes.len) {
const end = std.mem.indexOfScalarPos(u8, bytes, start, 0) orelse break;
const str = bytes[start..end];
try writer.print("[{d:04}] ", .{start});
for (str) |b| try writer.print("{x:02} ", .{b});
try writer.print("00: {s}\n", .{str});
start = end + 1;
}
for (ir.globals) |global| {
try writer.print("{any}\n", .{global});
}
return writer.flush();
}
pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void {
gpa.free(ir.string_bytes);
gpa.free(ir.instructions);
gpa.free(ir.globals);
gpa.free(ir.extra);
ir.* = undefined;
}
pub const Global = struct {
tag: Tag,
name: Ir.NullTerminatedString,
is_constant: bool,
pub const Tag = enum {
knot,
variable,
};
};
pub const Inst = struct {
tag: Tag,
data: Data,
/// An index to an IR instruction. Some values are reserved.
pub const Index = enum(u32) {
file_inst,
ref_start_index = 32,
@ -24,6 +130,15 @@ pub const Inst = struct {
}
};
/// A reference to an IR instruction, or to an statically interned value,
/// or neither.
///
/// If the integer tag value is < `Index.ref_start_index`, then it
/// corresponds to an interned value. Otherwise, this refers to a IR
/// instruction.
///
/// The tag type is specified so that it is safe to bitcast between `[]u32`
/// and `[]Ref`.
pub const Ref = enum(u32) {
bool_true,
bool_false,
@ -174,93 +289,13 @@ pub const Inst = struct {
obj_ptr: Ref,
field_name_start: NullTerminatedString,
};
};
pub const Global = struct {
tag: Tag,
name: Ir.NullTerminatedString,
is_constant: bool,
pub const CompileErrors = struct {
items_len: u32,
pub const Tag = enum {
knot,
variable,
};
};
pub const NullTerminatedString = enum(u32) {
empty,
_,
};
pub const IndexSlice = struct {
index: u32,
len: u32,
};
pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void {
gpa.free(ir.string_bytes);
gpa.free(ir.instructions);
gpa.free(ir.globals);
gpa.free(ir.extra);
gpa.free(ir.errors);
ir.* = undefined;
}
pub fn render(ir: Ir, writer: *std.Io.Writer) !void {
assert(ir.instructions.len > 0);
var w: Writer = .{ .code = ir };
try w.writeInst(writer, .file_inst);
return writer.flush();
}
pub fn dumpInfo(ir: Ir, writer: *std.Io.Writer) !void {
const bytes = ir.string_bytes;
var start: usize = 0;
while (start < bytes.len) {
const end = std.mem.indexOfScalarPos(u8, bytes, start, 0) orelse break;
const str = bytes[start..end];
try writer.print("[{d:04}] ", .{start});
for (str) |b| try writer.print("{x:02} ", .{b});
try writer.print("00: {s}\n", .{str});
start = end + 1;
}
for (ir.globals) |global| {
try writer.print("{any}\n", .{global});
}
return writer.flush();
}
fn ExtraData(comptime T: type) type {
return struct {
data: T,
end: usize,
};
}
pub fn extraData(ir: Ir, comptime T: type, index: usize) ExtraData(T) {
const fields = @typeInfo(T).@"struct".fields;
var i: usize = index;
var result: T = undefined;
inline for (fields) |field| {
@field(result, field.name) = switch (field.type) {
u32 => ir.extra[i],
Inst.Index => @enumFromInt(ir.extra[i]),
Inst.Ref => @enumFromInt(ir.extra[i]),
NullTerminatedString => @enumFromInt(ir.extra[i]),
else => @compileError("bad field type"),
pub const Item = struct {
msg: NullTerminatedString,
byte_offset: u32,
};
i += 1;
}
return .{ .data = result, .end = i };
}
pub fn nullTerminatedString(ir: Ir, index: NullTerminatedString) [:0]const u8 {
const slice = ir.string_bytes[@intFromEnum(index)..];
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
}
pub fn bodySlice(ir: Ir, start: usize, len: usize) []Inst.Index {
return @ptrCast(ir.extra[start..][0..len]);
}
};
};