feat: error message for unknown global variables

This commit is contained in:
Brett Broadhurst 2026-03-18 19:21:56 -06:00
parent c940374f27
commit 47351cd6f9
Failed to generate hash of commit
6 changed files with 184 additions and 89 deletions

View file

@ -128,7 +128,12 @@ fn nullTerminatedString(astgen: *AstGen, str: Ir.NullTerminatedString) [:0]const
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
}
fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.NullTerminatedString {
const IndexSlice = struct {
index: Ir.NullTerminatedString,
len: u32,
};
fn strFromSlice(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!IndexSlice {
const gpa = astgen.gpa;
const string_bytes = &astgen.string_bytes;
const str_index: u32 = @intCast(string_bytes.items.len);
@ -142,17 +147,23 @@ fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.Nul
});
if (gop.found_existing) {
string_bytes.shrinkRetainingCapacity(str_index);
return @enumFromInt(gop.key_ptr.*);
return .{
.index = @enumFromInt(gop.key_ptr.*),
.len = @intCast(key.len),
};
} else {
gop.key_ptr.* = str_index;
try string_bytes.append(gpa, 0);
return @enumFromInt(str_index);
return .{
.index = @enumFromInt(str_index),
.len = @intCast(key.len),
};
}
}
fn stringFromNode(astgen: *AstGen, node: *const Ast.Node) !Ir.NullTerminatedString {
fn strFromNode(astgen: *AstGen, node: *const Ast.Node) !IndexSlice {
const name_bytes = astgen.tree.nodeSlice(node);
return astgen.stringFromBytes(name_bytes);
return astgen.strFromSlice(name_bytes);
}
fn qualifiedString(
@ -332,14 +343,8 @@ const GenIr = struct {
}
fn addInt(gi: *GenIr, value: u64) !Ir.Inst.Ref {
return add(gi, .{ .tag = .integer, .data = .{
.integer = .{ .value = value },
} });
}
fn addStr(gi: *GenIr, tag: Ir.Inst.Tag, str: Ir.NullTerminatedString) !Ir.Inst.Ref {
return add(gi, .{ .tag = tag, .data = .{
.string = .{ .start = str },
return add(gi, .{ .tag = .int, .data = .{
.int = value,
} });
}
@ -360,14 +365,33 @@ const GenIr = struct {
} });
}
fn addDeclRef(gi: *GenIr, decl_ref: Ir.NullTerminatedString) !Ir.Inst.Ref {
return add(gi, .{ .tag = .decl_ref, .data = .{
.string = .{
.start = decl_ref,
},
fn addStr(
gi: *GenIr,
str: Ir.NullTerminatedString,
str_len: usize,
) !Ir.Inst.Ref {
assert(str_len <= std.math.maxInt(u32));
return add(gi, .{ .tag = .str, .data = .{
.str = .{ .start = str, .len = @intCast(str_len) },
} });
}
fn addStrTok(
block: *GenIr,
tag: Ir.Inst.Tag,
str_index: Ir.NullTerminatedString,
byte_offset: usize,
) !Ir.Inst.Ref {
assert(byte_offset <= std.math.maxInt(u32));
return block.add(.{
.tag = tag,
.data = .{ .str_tok = .{
.start = str_index,
.src_offset = @intCast(byte_offset),
} },
});
}
fn addPayloadNode(gi: *GenIr, tag: Ir.Inst.Tag, extra: anytype) !Ir.Inst.Ref {
const gpa = gi.astgen.gpa;
try gi.instructions.ensureUnusedCapacity(gpa, 1);
@ -675,13 +699,8 @@ fn numberLiteral(block: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
}
fn stringLiteral(gi: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
const str = try gi.astgen.stringFromNode(node);
return gi.add(.{
.tag = .string,
.data = .{ .string = .{
.start = str,
} },
});
const str = try gi.astgen.strFromNode(node);
return gi.addStr(str.index, str.len);
}
fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
@ -689,13 +708,16 @@ fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
return stringLiteral(gen, first_node);
}
fn identifier(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
const astgen = gi.astgen;
const str = try astgen.stringFromNode(node);
if (scope.lookup(str)) |decl| {
return gi.addUnaryNode(.load, decl.inst_index.toRef());
fn identifier(
block: *GenIr,
scope: *Scope,
node: *const Ast.Node,
) InnerError!Ir.Inst.Ref {
const str = try block.astgen.strFromNode(node);
if (scope.lookup(str.index)) |decl| {
return block.addUnaryNode(.load, decl.inst_index.toRef());
}
return gi.addDeclRef(str);
return block.addStrTok(.decl_ref, str.index, node.loc.start);
}
fn expr(gi: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Ref {
@ -1014,10 +1036,10 @@ fn assignStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void
const astgen = gi.astgen;
const identifier_node = node.data.bin.lhs.?;
const expr_node = node.data.bin.rhs.?;
const name_ref = try astgen.stringFromNode(identifier_node);
const name_str = try astgen.strFromNode(identifier_node);
// TODO: Support globals as well
if (scope.lookup(name_ref)) |decl| {
if (scope.lookup(name_str.index)) |decl| {
const expr_result = try expr(gi, scope, expr_node);
_ = try gi.addBinaryNode(.store, decl.inst_index.toRef(), expr_result);
return;
@ -1112,12 +1134,12 @@ fn fieldAccess(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.I
const data = node.data.bin;
assert(data.rhs.?.tag == .identifier);
const field_str = try gi.astgen.stringFromNode(data.rhs.?);
const field_str = try gi.astgen.strFromNode(data.rhs.?);
const lhs = try expr(gi, scope, data.lhs.?);
return gi.addPayloadNode(.field_ptr, Ir.Inst.Field{
.lhs = lhs,
.field_name_start = field_str,
.field_name_start = field_str.index,
});
}
@ -1133,10 +1155,10 @@ fn calleeExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Calle
const call_target = data.rhs.?;
assert(call_target.tag == .identifier);
const field_str = try gi.astgen.stringFromNode(call_target);
const field_str = try gi.astgen.strFromNode(call_target);
const lhs = try expr(gi, scope, data.lhs.?);
return .{
.field = .{ .obj_ptr = lhs, .field_name_start = field_str },
.field = .{ .obj_ptr = lhs, .field_name_start = field_str.index },
};
},
.identifier => {
@ -1265,9 +1287,9 @@ fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const astgen = gi.astgen;
const identifier_node = decl_node.data.bin.lhs.?;
const expr_node = decl_node.data.bin.rhs.?;
const name_ref = try astgen.stringFromNode(identifier_node);
const name_str = try astgen.strFromNode(identifier_node);
if (scope.lookup(name_ref)) |_| {
if (scope.lookup(name_str.index)) |_| {
return fail(astgen, decl_node, "duplicate identifier", .{});
}
@ -1275,7 +1297,7 @@ fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const expr_result = try expr(gi, scope, expr_node);
_ = try gi.addBinaryNode(.store, alloc_inst, expr_result);
return scope.insert(name_ref, .{
return scope.insert(name_str.index, .{
.decl_node = decl_node,
.inst_index = alloc_inst.toIndex().?,
});
@ -1297,9 +1319,11 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
_ = try expr(&decl_block, scope, expr_node);
const var_inst = try decl_block.addVar();
const name_str = try astgen.strFromNode(identifier_node);
try setDeclaration(decl_inst.toIndex().?, .{
.tag = .variable,
.name = try astgen.stringFromNode(identifier_node),
.name = name_str.index,
.ref = var_inst,
.decl_node = decl_node,
.body_block = &decl_block,
@ -1353,10 +1377,11 @@ fn defaultBlock(
const block_stmts = body_node.data.list.items.?;
try blockInner(&decl_scope, scope, block_stmts);
const name_str = try astgen.strFromSlice(Story.default_knot_name);
const knot_inst = try decl_scope.addKnot();
try setDeclaration(decl_inst, .{
.tag = .knot,
.name = try astgen.stringFromBytes(Story.default_knot_name),
.name = name_str.index,
.ref = knot_inst,
.decl_node = body_node,
.body_block = &decl_scope,
@ -1387,11 +1412,11 @@ fn stitchDeclInner(
const args_list = args_node.data.list.items.?;
for (args_list) |arg| {
assert(arg.tag == .parameter_decl);
const arg_str = try astgen.stringFromNode(arg);
const arg_inst = try decl_block.addStr(.param, arg_str);
const arg_str = try astgen.strFromNode(arg);
const arg_inst = try decl_block.addStrTok(.param, arg_str.index, arg.loc.start);
// TODO: Maybe make decl accept a ref?
try scope.insert(arg_str, .{
try scope.insert(arg_str.index, .{
.decl_node = arg,
.inst_index = arg_inst.toIndex().?,
});
@ -1403,12 +1428,11 @@ fn stitchDeclInner(
_ = try decl_block.addUnaryNode(.implicit_ret, .none);
}
const knot_inst = try decl_block.addKnot();
const name_str = try astgen.stringFromNode(identifier_node);
const name_str = try astgen.strFromNode(identifier_node);
try setDeclaration(decl_inst, .{
.tag = .knot,
.name = try astgen.qualifiedString(scope.namespace_prefix, name_str),
.ref = knot_inst,
.name = try astgen.qualifiedString(scope.namespace_prefix, name_str.index),
.ref = try decl_block.addKnot(),
.decl_node = decl_node,
.body_block = &decl_block,
});
@ -1444,8 +1468,8 @@ fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerE
try stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, null);
}
const name_str = try gi.astgen.stringFromNode(identifier_node);
try decl_scope.setNamespacePrefix(name_str);
const name_str = try gi.astgen.strFromNode(identifier_node);
try decl_scope.setNamespacePrefix(name_str.index);
for (nested_decls_list[start_index..]) |nested_decl_node| {
switch (nested_decl_node.tag) {