Skip to content

Commit fea8659

Browse files
committed
stage2: comptime function calls
* Function calls that happen in a comptime scope get called at compile-time. We do this by putting the parameters in place as constant values and then running regular function analysis on the body. * Added `Scope.Block.dump()` for debugging purposes. * Fixed some code to call `identifierTokenString` rather than `tokenSlice`, making it work for `@""` syntax. * Implemented `Value.copy` for big integers. Follow-up issues to tackle: * Adding compile errors to the callsite instead of the callee Decl. * Proper error notes for "called from here". - Related: ziglang#7555 * Branch quotas. * ZIR support?
1 parent fb37c1b commit fea8659

File tree

6 files changed

+257
-46
lines changed

6 files changed

+257
-46
lines changed

src/Module.zig

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ pub const Decl = struct {
268268
}
269269
}
270270

271+
/// Asserts that the `Decl` is part of AST and not ZIRModule.
272+
pub fn getFileScope(self: *Decl) *Scope.File {
273+
return self.scope.cast(Scope.Container).?.file_scope;
274+
}
275+
271276
fn removeDependant(self: *Decl, other: *Decl) void {
272277
self.dependants.removeAssertDiscard(other);
273278
}
@@ -776,6 +781,11 @@ pub const Scope = struct {
776781
results: ArrayListUnmanaged(*Inst),
777782
block_inst: *Inst.Block,
778783
};
784+
785+
/// For debugging purposes.
786+
pub fn dump(self: *Block, mod: Module) void {
787+
zir.dumpBlock(mod, self);
788+
}
779789
};
780790

781791
/// This is a temporary structure, references to it are valid only
@@ -992,11 +1002,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
9921002
defer tracy.end();
9931003

9941004
const container_scope = decl.scope.cast(Scope.Container).?;
995-
const tree = try self.getAstTree(container_scope);
1005+
const tree = try self.getAstTree(container_scope.file_scope);
9961006
const ast_node = tree.root_node.decls()[decl.src_index];
9971007
switch (ast_node.tag) {
9981008
.FnProto => {
999-
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
1009+
const fn_proto = ast_node.castTag(.FnProto).?;
10001010

10011011
decl.analysis = .in_progress;
10021012

@@ -1131,7 +1141,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11311141
for (fn_proto.params()) |param, i| {
11321142
const name_token = param.name_token.?;
11331143
const src = tree.token_locs[name_token].start;
1134-
const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
1144+
const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
11351145
const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
11361146
arg.* = .{
11371147
.base = .{
@@ -1496,12 +1506,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
14961506
}
14971507
}
14981508

1499-
fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
1509+
pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
15001510
const tracy = trace(@src());
15011511
defer tracy.end();
15021512

1503-
const root_scope = container_scope.file_scope;
1504-
15051513
switch (root_scope.status) {
15061514
.never_loaded, .unloaded_success => {
15071515
try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
@@ -1549,7 +1557,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
15491557

15501558
// We may be analyzing it for the first time, or this may be
15511559
// an incremental update. This code handles both cases.
1552-
const tree = try self.getAstTree(container_scope);
1560+
const tree = try self.getAstTree(container_scope.file_scope);
15531561
const decls = tree.root_node.decls();
15541562

15551563
try self.comp.work_queue.ensureUnusedCapacity(decls.len);
@@ -3427,3 +3435,23 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
34273435
return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
34283436
}
34293437
}
3438+
3439+
/// Identifier token -> String (allocated in scope.arena())
3440+
pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
3441+
const tree = scope.tree();
3442+
3443+
const ident_name = tree.tokenSlice(token);
3444+
if (mem.startsWith(u8, ident_name, "@")) {
3445+
const raw_string = ident_name[1..];
3446+
var bad_index: usize = undefined;
3447+
return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
3448+
error.InvalidCharacter => {
3449+
const bad_byte = raw_string[bad_index];
3450+
const src = tree.token_locs[token].start;
3451+
return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
3452+
},
3453+
else => |e| return e,
3454+
};
3455+
}
3456+
return ident_name;
3457+
}

src/astgen.zig

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
384384
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
385385
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
386386
else => if (node.getLabel()) |break_label| {
387-
const label_name = try identifierTokenString(mod, parent_scope, break_label);
387+
const label_name = try mod.identifierTokenString(parent_scope, break_label);
388388
return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
389389
} else {
390390
return mod.failTok(parent_scope, src, "break expression outside loop", .{});
@@ -426,7 +426,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
426426
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
427427
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
428428
else => if (node.getLabel()) |break_label| {
429-
const label_name = try identifierTokenString(mod, parent_scope, break_label);
429+
const label_name = try mod.identifierTokenString(parent_scope, break_label);
430430
return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
431431
} else {
432432
return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
@@ -551,7 +551,7 @@ fn varDecl(
551551
}
552552
const tree = scope.tree();
553553
const name_src = tree.token_locs[node.name_token].start;
554-
const ident_name = try identifierTokenString(mod, scope, node.name_token);
554+
const ident_name = try mod.identifierTokenString(scope, node.name_token);
555555

556556
// Local variables shadowing detection, including function parameters.
557557
{
@@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins
843843
fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
844844
const tree = scope.tree();
845845
const src = tree.token_locs[node.name].start;
846-
const name = try identifierTokenString(mod, scope, node.name);
846+
const name = try mod.identifierTokenString(scope, node.name);
847847

848848
return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
849849
}
@@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro
864864

865865
for (decls) |decl, i| {
866866
const tag = decl.castTag(.ErrorTag).?;
867-
fields[i] = try identifierTokenString(mod, scope, tag.name_token);
867+
fields[i] = try mod.identifierTokenString(scope, tag.name_token);
868868
}
869869

870870
// analyzing the error set results in a decl ref, so we might need to dereference it
@@ -988,36 +988,16 @@ fn orelseCatchExpr(
988988
/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
989989
/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
990990
fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
991-
const ident_name_1 = try identifierTokenString(mod, scope, token1);
992-
const ident_name_2 = try identifierTokenString(mod, scope, token2);
991+
const ident_name_1 = try mod.identifierTokenString(scope, token1);
992+
const ident_name_2 = try mod.identifierTokenString(scope, token2);
993993
return mem.eql(u8, ident_name_1, ident_name_2);
994994
}
995995

996-
/// Identifier token -> String (allocated in scope.arena())
997-
fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
998-
const tree = scope.tree();
999-
1000-
const ident_name = tree.tokenSlice(token);
1001-
if (mem.startsWith(u8, ident_name, "@")) {
1002-
const raw_string = ident_name[1..];
1003-
var bad_index: usize = undefined;
1004-
return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
1005-
error.InvalidCharacter => {
1006-
const bad_byte = raw_string[bad_index];
1007-
const src = tree.token_locs[token].start;
1008-
return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
1009-
},
1010-
else => |e| return e,
1011-
};
1012-
}
1013-
return ident_name;
1014-
}
1015-
1016996
pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
1017997
const tree = scope.tree();
1018998
const src = tree.token_locs[node.token].start;
1019999

1020-
const ident_name = try identifierTokenString(mod, scope, node.token);
1000+
const ident_name = try mod.identifierTokenString(scope, node.token);
10211001

10221002
return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
10231003
}
@@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
19361916
defer tracy.end();
19371917

19381918
const tree = scope.tree();
1939-
const ident_name = try identifierTokenString(mod, scope, ident.token);
1919+
const ident_name = try mod.identifierTokenString(scope, ident.token);
19401920
const src = tree.token_locs[ident.token].start;
19411921
if (mem.eql(u8, ident_name, "_")) {
19421922
return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});

src/value.zig

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,14 @@ pub const Value = extern union {
330330
.int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
331331
.int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),
332332
.int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),
333-
.int_big_positive => {
334-
@panic("TODO implement copying of big ints");
335-
},
336-
.int_big_negative => {
337-
@panic("TODO implement copying of big ints");
333+
.int_big_positive, .int_big_negative => {
334+
const old_payload = self.cast(Payload.BigInt).?;
335+
const new_payload = try allocator.create(Payload.BigInt);
336+
new_payload.* = .{
337+
.base = .{ .tag = self.ptr_otherwise.tag },
338+
.data = try allocator.dupe(std.math.big.Limb, old_payload.data),
339+
};
340+
return Value{ .ptr_otherwise = &new_payload.base };
338341
},
339342
.function => return self.copyPayloadShallow(allocator, Payload.Function),
340343
.extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),

src/zir.zig

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,46 @@ pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
18851885
module.dump();
18861886
}
18871887

1888+
/// For debugging purposes, prints a function representation to stderr.
1889+
pub fn dumpBlock(old_module: IrModule, module_block: *IrModule.Scope.Block) void {
1890+
const allocator = old_module.gpa;
1891+
var ctx: EmitZIR = .{
1892+
.allocator = allocator,
1893+
.decls = .{},
1894+
.arena = std.heap.ArenaAllocator.init(allocator),
1895+
.old_module = &old_module,
1896+
.next_auto_name = 0,
1897+
.names = std.StringArrayHashMap(void).init(allocator),
1898+
.primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1899+
.indent = 0,
1900+
.block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1901+
.loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
1902+
.metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
1903+
.body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
1904+
};
1905+
defer ctx.metadata.deinit();
1906+
defer ctx.body_metadata.deinit();
1907+
defer ctx.block_table.deinit();
1908+
defer ctx.loop_table.deinit();
1909+
defer ctx.decls.deinit(allocator);
1910+
defer ctx.names.deinit();
1911+
defer ctx.primitive_table.deinit();
1912+
defer ctx.arena.deinit();
1913+
1914+
_ = ctx.emitBlock(module_block, 0) catch |err| {
1915+
std.debug.print("unable to dump function: {}\n", .{err});
1916+
return;
1917+
};
1918+
var module = Module{
1919+
.decls = ctx.decls.items,
1920+
.arena = ctx.arena,
1921+
.metadata = ctx.metadata,
1922+
.body_metadata = ctx.body_metadata,
1923+
};
1924+
1925+
module.dump();
1926+
}
1927+
18881928
const EmitZIR = struct {
18891929
allocator: *Allocator,
18901930
arena: std.heap.ArenaAllocator,
@@ -2065,6 +2105,36 @@ const EmitZIR = struct {
20652105
return &declref_inst.base;
20662106
}
20672107

2108+
fn emitBlock(self: *EmitZIR, module_block: *IrModule.Scope.Block, src: usize) Allocator.Error!*Decl {
2109+
var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
2110+
defer inst_table.deinit();
2111+
2112+
var instructions = std.ArrayList(*Inst).init(self.allocator);
2113+
defer instructions.deinit();
2114+
2115+
const body: ir.Body = .{ .instructions = module_block.instructions.items };
2116+
try self.emitBody(body, &inst_table, &instructions);
2117+
2118+
const fn_type = try self.emitType(src, Type.initTag(.void));
2119+
2120+
const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
2121+
mem.copy(*Inst, arena_instrs, instructions.items);
2122+
2123+
const fn_inst = try self.arena.allocator.create(Inst.Fn);
2124+
fn_inst.* = .{
2125+
.base = .{
2126+
.src = src,
2127+
.tag = Inst.Fn.base_tag,
2128+
},
2129+
.positionals = .{
2130+
.fn_type = fn_type.inst,
2131+
.body = .{ .instructions = arena_instrs },
2132+
},
2133+
.kw_args = .{},
2134+
};
2135+
return self.emitUnnamedDecl(&fn_inst.base);
2136+
}
2137+
20682138
fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
20692139
var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
20702140
defer inst_table.deinit();

0 commit comments

Comments
 (0)