feat: error reporting for global reference usage

This commit is contained in:
Brett Broadhurst 2026-03-19 01:54:33 -06:00
parent 47351cd6f9
commit be297047d1
Failed to generate hash of commit
8 changed files with 401 additions and 371 deletions

View file

@ -392,30 +392,50 @@ const GenIr = struct {
});
}
fn addPayloadNode(gi: *GenIr, tag: Ir.Inst.Tag, extra: anytype) !Ir.Inst.Ref {
const gpa = gi.astgen.gpa;
try gi.instructions.ensureUnusedCapacity(gpa, 1);
try gi.astgen.instructions.ensureUnusedCapacity(gpa, 1);
fn addPayloadNode(
gen: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
extra: anytype,
) !Ir.Inst.Ref {
const gpa = gen.astgen.gpa;
try gen.instructions.ensureUnusedCapacity(gpa, 1);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const payload_index = try gi.astgen.addExtra(extra);
const new_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
gi.astgen.instructions.appendAssumeCapacity(.{
const extra_index = try gen.astgen.addExtra(extra);
const new_index: Ir.Inst.Index = @enumFromInt(gen.astgen.instructions.items.len);
gen.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .payload = .{
.payload_index = payload_index,
.extra_index = extra_index,
.src_offset = @intCast(node.loc.start),
} },
});
gi.instructions.appendAssumeCapacity(new_index);
gen.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn addPayloadNodeWithIndex(
gen: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
extra_index: u32,
) !Ir.Inst.Ref {
return gen.add(.{ .tag = tag, .data = .{
.payload = .{
.extra_index = extra_index,
.src_offset = @intCast(node.loc.start),
},
} });
}
fn makeBlockInst(gi: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const inst_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
const gpa = gi.astgen.gpa;
try gi.astgen.instructions.append(gpa, .{
.tag = tag,
.data = .{
.payload = .{ .payload_index = undefined },
.payload = undefined,
},
});
return inst_index;
@ -431,11 +451,11 @@ const GenIr = struct {
try self.astgen.instructions.append(gpa, .{
.tag = .decl_knot,
.data = .{
.payload = .{ .payload_index = undefined },
.payload = undefined,
},
});
const inst_data = &self.astgen.instructions.items[@intFromEnum(new_index)].data;
inst_data.payload.payload_index = self.astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Knot{ .body_len = @intCast(body.len) },
);
@ -443,75 +463,50 @@ const GenIr = struct {
return new_index;
}
fn addVar(self: *GenIr) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
const body = self.instructionsSlice();
fn addVar(gen: *GenIr, node: *const Ast.Node) !Ir.Inst.Index {
const gpa = gen.astgen.gpa;
const body = gen.instructionsSlice();
const extra_len = @typeInfo(Ir.Inst.Var).@"struct".fields.len + body.len;
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try self.astgen.instructions.ensureUnusedCapacity(gpa, 1);
try self.instructions.ensureUnusedCapacity(gpa, 1);
try gen.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
self.astgen.instructions.appendAssumeCapacity(.{ .tag = .decl_var, .data = .{
.payload = .{ .payload_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Var{ .body_len = @intCast(body.len) },
) },
} });
self.astgen.appendBlockBody(body);
self.instructions.appendAssumeCapacity(new_index);
return new_index;
const extra_index = gen.astgen.addExtraAssumeCapacity(Ir.Inst.Var{
.body_len = @intCast(body.len),
});
const new_index = try gen.addPayloadNodeWithIndex(.decl_var, node, extra_index);
gen.astgen.appendBlockBody(body);
gen.instructions.appendAssumeCapacity(new_index.toIndex().?);
return new_index.toIndex().?;
}
fn addCondBr(self: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
try self.instructions.ensureUnusedCapacity(gpa, 1);
try self.astgen.instructions.ensureUnusedCapacity(gpa, 1);
fn addCondBr(gen: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const gpa = gen.astgen.gpa;
try gen.instructions.ensureUnusedCapacity(gpa, 1);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
self.astgen.instructions.appendAssumeCapacity(.{
const new_index: Ir.Inst.Index = @enumFromInt(gen.astgen.instructions.items.len);
gen.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
self.instructions.appendAssumeCapacity(new_index);
gen.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn addBreak(
self: *GenIr,
gen: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
block_inst: Ir.Inst.Index,
) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
try self.instructions.ensureUnusedCapacity(gpa, 1);
const new_index = try self.makeBreak(tag, block_inst);
self.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn makeBreak(
self: *GenIr,
tag: Ir.Inst.Tag,
block_inst: Ir.Inst.Index,
) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
) !Ir.Inst.Ref {
const gpa = gen.astgen.gpa;
const extra_len = @typeInfo(Ir.Inst.Break).@"struct".fields.len;
try self.astgen.instructions.ensureUnusedCapacity(gpa, 1);
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try gen.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
self.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .payload = .{
.payload_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Break{ .block_inst = block_inst },
),
} },
});
return new_index;
const extra_index = gen.astgen.addExtraAssumeCapacity(
Ir.Inst.Break{ .block_inst = block_inst },
);
return gen.addPayloadNodeWithIndex(tag, node, extra_index);
}
fn setBlockBody(self: *GenIr, inst: Ir.Inst.Index) !void {
@ -521,7 +516,7 @@ const GenIr = struct {
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
const inst_data = &self.astgen.instructions.items[@intFromEnum(inst)].data;
inst_data.payload.payload_index = self.astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Block{ .body_len = @intCast(body.len) },
);
self.astgen.appendBlockBody(body);
@ -599,7 +594,7 @@ fn setDeclaration(
}
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.Declaration{ .name = args.name, .value = args.ref },
);
@ -630,7 +625,7 @@ fn setCondBrPayload(
try astgen.extra.ensureUnusedCapacity(astgen.gpa, extra_len);
const inst_data = &astgen.instructions.items[@intFromEnum(condbr)].data;
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.CondBr{
.condition = cond,
.then_body_len = @intCast(then_body_len),
@ -856,13 +851,13 @@ fn ifStmt(
defer then_block.unstack();
try blockStmt(&then_block, scope, then_node);
_ = try then_block.addBreak(.@"break", block);
_ = try then_block.addBreak(.@"break", then_node, block);
var else_block = parent_block.makeSubBlock();
defer else_block.unstack();
if (then_node == last_prong) {
_ = try else_block.addBreak(.@"break", block);
_ = try else_block.addBreak(.@"break", then_node, block);
} else {
const block_node = last_prong.data.bin.rhs.?;
try blockStmt(&else_block, scope, block_node);
@ -900,13 +895,13 @@ fn ifChain(
var then_block = parent_block.makeSubBlock();
defer then_block.unstack();
try blockStmt(&then_block, scope, body_node);
_ = try then_block.addBreak(.@"break", block_inst);
_ = try then_block.addBreak(.@"break", body_node, block_inst);
var else_block = parent_block.makeSubBlock();
defer else_block.unstack();
const next_branches = branch_list[1..];
_ = try ifChain(parent_block, scope, next_branches);
_ = try else_block.addBreak(.@"break", block_inst);
_ = try else_block.addBreak(.@"break", body_node, block_inst);
try setCondBrPayload(condbr, cond_inst, &then_block, &else_block);
return @enumFromInt(0);
}
@ -960,7 +955,7 @@ fn switchStmt(
var case_block = parent_block.makeSubBlock();
defer case_block.unstack();
_ = try blockStmt(&case_block, scope, case_stmt.data.bin.rhs.?);
_ = try case_block.addBreak(.@"break", switch_br);
_ = try case_block.addBreak(.@"break", case_stmt, switch_br);
const body = case_block.instructionsSlice();
const case_extra_len = @typeInfo(Ir.Inst.SwitchBr.Case).@"struct".fields.len + body.len;
@ -982,7 +977,7 @@ fn switchStmt(
var case_block = parent_block.makeSubBlock();
defer case_block.unstack();
_ = try blockStmt(&case_block, scope, else_branch.data.bin.rhs.?);
_ = try case_block.addBreak(.@"break", switch_br);
_ = try case_block.addBreak(.@"break", else_branch, switch_br);
const else_body = case_block.instructionsSlice();
const extra_len =
@ -990,13 +985,14 @@ fn switchStmt(
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
astgen.instructions.items[@intFromEnum(switch_br)].data.payload = .{
.payload_index = astgen.addExtraAssumeCapacity(
.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.SwitchBr{
.operand = cond_inst,
.cases_len = @intCast(switch_cases.len),
.else_body_len = @intCast(else_body.len),
},
),
.src_offset = @intCast(stmt_node.loc.start),
};
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
astgen.appendBlockBody(else_body);
@ -1111,11 +1107,12 @@ fn choiceStmt(
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
astgen.instructions.items[@intFromEnum(choice_br)].data.payload = .{
.payload_index = astgen.addExtraAssumeCapacity(
.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.ChoiceBr{
.cases_len = @intCast(choice_branches.len),
},
),
.src_offset = @intCast(stmt_node.loc.start),
};
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
}
@ -1132,12 +1129,12 @@ const Callee = union(enum) {
fn fieldAccess(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
assert(node.tag == .selector_expr);
const data = node.data.bin;
assert(data.rhs.?.tag == .identifier);
const field_str = try gi.astgen.strFromNode(data.rhs.?);
const rhs = data.rhs.?;
assert(rhs.tag == .identifier);
const field_str = try gi.astgen.strFromNode(rhs);
const lhs = try expr(gi, scope, data.lhs.?);
return gi.addPayloadNode(.field_ptr, Ir.Inst.Field{
return gi.addPayloadNode(.field_ptr, rhs, Ir.Inst.Field{
.lhs = lhs,
.field_name_start = field_str.index,
});
@ -1205,22 +1202,19 @@ fn callExpr(
}
switch (callee) {
.direct => |callee_obj| {
const payload_index = try addExtra(astgen, Ir.Inst.Call{
const tag = if (call == .divert) .divert else .call;
const extra_index = try addExtra(astgen, Ir.Inst.Call{
.callee = callee_obj,
.args_len = @intCast(args_count),
});
if (args_count != 0) {
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
}
return gi.add(.{
.tag = if (call == .divert) .divert else .call,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
return gi.addPayloadNodeWithIndex(tag, callee_node, extra_index);
},
.field => |callee_field| {
const payload_index = try addExtra(astgen, Ir.Inst.FieldCall{
const tag = if (call == .divert) .field_divert else .field_call;
const extra_index = try addExtra(astgen, Ir.Inst.FieldCall{
.obj_ptr = callee_field.obj_ptr,
.field_name_start = callee_field.field_name_start,
.args_len = @intCast(args_count),
@ -1228,12 +1222,7 @@ fn callExpr(
if (args_count != 0) {
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
}
return gi.add(.{
.tag = if (call == .divert) .field_divert else .field_call,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
return gi.addPayloadNodeWithIndex(tag, callee_node, extra_index);
},
}
}
@ -1246,29 +1235,17 @@ fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
const callee = try calleeExpr(gi, scope, lhs);
switch (callee) {
.direct => |callee_obj| {
const payload_index = try addExtra(gi.astgen, Ir.Inst.Call{
_ = try gi.addPayloadNode(.divert, lhs, Ir.Inst.Call{
.callee = callee_obj,
.args_len = @intCast(0),
});
_ = try gi.add(.{
.tag = .divert,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
},
.field => |callee_field| {
const payload_index = try addExtra(gi.astgen, Ir.Inst.FieldCall{
_ = try gi.addPayloadNode(.field_divert, lhs, Ir.Inst.FieldCall{
.obj_ptr = callee_field.obj_ptr,
.field_name_start = callee_field.field_name_start,
.args_len = @intCast(0),
});
_ = try gi.add(.{
.tag = .field_divert,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
},
}
},
@ -1309,17 +1286,14 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const expr_node = decl_node.data.bin.rhs.?;
const decl_inst = try gi.add(.{
.tag = .declaration,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
var decl_block = gi.makeSubBlock();
defer decl_block.unstack();
_ = try expr(&decl_block, scope, expr_node);
const var_inst = try decl_block.addVar();
const var_inst = try decl_block.addVar(decl_node);
const name_str = try astgen.strFromNode(identifier_node);
try setDeclaration(decl_inst.toIndex().?, .{
.tag = .variable,
@ -1365,9 +1339,7 @@ fn defaultBlock(
const astgen = gi.astgen;
const decl_inst = try gi.addAsIndex(.{
.tag = .declaration,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
var decl_scope = gi.makeSubBlock();
@ -1400,9 +1372,7 @@ fn stitchDeclInner(
const identifier_node = prototype_data.lhs.?;
const decl_inst = try gi.addAsIndex(.{
.tag = .declaration,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
var decl_block = gi.makeSubBlock();
@ -1440,12 +1410,12 @@ fn stitchDeclInner(
fn stitchDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void {
const knot_data = decl_node.data.bin;
const prototype_node = knot_data.lhs.?;
const body_node = knot_data.rhs.?;
const prototype_node = knot_data.lhs;
const body_node = knot_data.rhs;
var decl_scope = parent_scope.makeChild();
defer decl_scope.deinit();
return stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, body_node);
return stitchDeclInner(gi, &decl_scope, decl_node, prototype_node.?, body_node);
}
fn functionDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
@ -1484,7 +1454,7 @@ fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
const file_inst = try gi.addAsIndex(.{
.tag = .file,
.data = .{
.payload = .{ .payload_index = undefined },
.payload = undefined,
},
});