authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-01 19:24:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 19:10:11-07:00
logfea8659b82ea1a785f933c58ba9d65ceb05a4094
tree91d33abe8ae357bc5aecc06e9fd2423bd83a10a8
parentfb37c1b0912c65d72b82f32df8bc7e780ab1ad80

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: #7555 * Branch quotas. * ZIR support?

6 files changed, 257 insertions(+), 46 deletions(-)

src/Module.zig+35-7
...@@ -268,6 +268,11 @@ pub const Decl = struct {...@@ -268,6 +268,11 @@ pub const Decl = struct {
268 }268 }
269 }269 }
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
271 fn removeDependant(self: *Decl, other: *Decl) void {276 fn removeDependant(self: *Decl, other: *Decl) void {
272 self.dependants.removeAssertDiscard(other);277 self.dependants.removeAssertDiscard(other);
273 }278 }
...@@ -776,6 +781,11 @@ pub const Scope = struct {...@@ -776,6 +781,11 @@ pub const Scope = struct {
776 results: ArrayListUnmanaged(*Inst),781 results: ArrayListUnmanaged(*Inst),
777 block_inst: *Inst.Block,782 block_inst: *Inst.Block,
778 };783 };
784
785 /// For debugging purposes.
786 pub fn dump(self: *Block, mod: Module) void {
787 zir.dumpBlock(mod, self);
788 }
779 };789 };
780790
781 /// This is a temporary structure, references to it are valid only791 /// This is a temporary structure, references to it are valid only
...@@ -992,11 +1002,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -992,11 +1002,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
992 defer tracy.end();1002 defer tracy.end();
9931003
994 const container_scope = decl.scope.cast(Scope.Container).?;1004 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);
996 const ast_node = tree.root_node.decls()[decl.src_index];1006 const ast_node = tree.root_node.decls()[decl.src_index];
997 switch (ast_node.tag) {1007 switch (ast_node.tag) {
998 .FnProto => {1008 .FnProto => {
999 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);1009 const fn_proto = ast_node.castTag(.FnProto).?;
10001010
1001 decl.analysis = .in_progress;1011 decl.analysis = .in_progress;
10021012
...@@ -1131,7 +1141,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1131,7 +1141,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1131 for (fn_proto.params()) |param, i| {1141 for (fn_proto.params()) |param, i| {
1132 const name_token = param.name_token.?;1142 const name_token = param.name_token.?;
1133 const src = tree.token_locs[name_token].start;1143 const src = tree.token_locs[name_token].start;
1134 const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString1144 const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
1135 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);1145 const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
1136 arg.* = .{1146 arg.* = .{
1137 .base = .{1147 .base = .{
...@@ -1496,12 +1506,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {...@@ -1496,12 +1506,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
1496 }1506 }
1497}1507}
14981508
1499fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {1509pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1500 const tracy = trace(@src());1510 const tracy = trace(@src());
1501 defer tracy.end();1511 defer tracy.end();
15021512
1503 const root_scope = container_scope.file_scope;
1504
1505 switch (root_scope.status) {1513 switch (root_scope.status) {
1506 .never_loaded, .unloaded_success => {1514 .never_loaded, .unloaded_success => {
1507 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);1515 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...@@ -1549,7 +1557,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
15491557
1550 // We may be analyzing it for the first time, or this may be1558 // We may be analyzing it for the first time, or this may be
1551 // an incremental update. This code handles both cases.1559 // 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);
1553 const decls = tree.root_node.decls();1561 const decls = tree.root_node.decls();
15541562
1555 try self.comp.work_queue.ensureUnusedCapacity(decls.len);1563 try self.comp.work_queue.ensureUnusedCapacity(decls.len);
...@@ -3427,3 +3435,23 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void...@@ -3427,3 +3435,23 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
3427 return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});3435 return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
3428 }3436 }
3429}3437}
3438
3439/// Identifier token -> String (allocated in scope.arena())
3440pub 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+9-29
...@@ -384,7 +384,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr...@@ -384,7 +384,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
384 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,384 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
385 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,385 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
386 else => if (node.getLabel()) |break_label| {386 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);
388 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});388 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
389 } else {389 } else {
390 return mod.failTok(parent_scope, src, "break expression outside loop", .{});390 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...@@ -426,7 +426,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
426 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,426 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
427 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,427 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
428 else => if (node.getLabel()) |break_label| {428 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);
430 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});430 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
431 } else {431 } else {
432 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});432 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
...@@ -551,7 +551,7 @@ fn varDecl(...@@ -551,7 +551,7 @@ fn varDecl(
551 }551 }
552 const tree = scope.tree();552 const tree = scope.tree();
553 const name_src = tree.token_locs[node.name_token].start;553 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
556 // Local variables shadowing detection, including function parameters.556 // Local variables shadowing detection, including function parameters.
557 {557 {
...@@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins...@@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins
843fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {843fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
844 const tree = scope.tree();844 const tree = scope.tree();
845 const src = tree.token_locs[node.name].start;845 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
848 return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});848 return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
849}849}
...@@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro...@@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro
864864
865 for (decls) |decl, i| {865 for (decls) |decl, i| {
866 const tag = decl.castTag(.ErrorTag).?;866 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);
868 }868 }
869869
870 // analyzing the error set results in a decl ref, so we might need to dereference it870 // analyzing the error set results in a decl ref, so we might need to dereference it
...@@ -988,36 +988,16 @@ fn orelseCatchExpr(...@@ -988,36 +988,16 @@ fn orelseCatchExpr(
988/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.988/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
989/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.989/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
990fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {990fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
991 const ident_name_1 = try identifierTokenString(mod, scope, token1);991 const ident_name_1 = try mod.identifierTokenString(scope, token1);
992 const ident_name_2 = try identifierTokenString(mod, scope, token2);992 const ident_name_2 = try mod.identifierTokenString(scope, token2);
993 return mem.eql(u8, ident_name_1, ident_name_2);993 return mem.eql(u8, ident_name_1, ident_name_2);
994}994}
995995
996/// Identifier token -> String (allocated in scope.arena())
997fn 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
1016pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {996pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
1017 const tree = scope.tree();997 const tree = scope.tree();
1018 const src = tree.token_locs[node.token].start;998 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
1022 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});1002 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
1023}1003}
...@@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
1936 defer tracy.end();1916 defer tracy.end();
19371917
1938 const tree = scope.tree();1918 const tree = scope.tree();
1939 const ident_name = try identifierTokenString(mod, scope, ident.token);1919 const ident_name = try mod.identifierTokenString(scope, ident.token);
1940 const src = tree.token_locs[ident.token].start;1920 const src = tree.token_locs[ident.token].start;
1941 if (mem.eql(u8, ident_name, "_")) {1921 if (mem.eql(u8, ident_name, "_")) {
1942 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});1922 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
src/value.zig+8-5
...@@ -330,11 +330,14 @@ pub const Value = extern union {...@@ -330,11 +330,14 @@ pub const Value = extern union {
330 .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),330 .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
331 .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),331 .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),
332 .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),332 .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),
333 .int_big_positive => {333 .int_big_positive, .int_big_negative => {
334 @panic("TODO implement copying of big ints");334 const old_payload = self.cast(Payload.BigInt).?;
335 },335 const new_payload = try allocator.create(Payload.BigInt);
336 .int_big_negative => {336 new_payload.* = .{
337 @panic("TODO implement copying of big ints");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 };
338 },341 },
339 .function => return self.copyPayloadShallow(allocator, Payload.Function),342 .function => return self.copyPayloadShallow(allocator, Payload.Function),
340 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),343 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),
src/zir.zig+70
...@@ -1885,6 +1885,46 @@ pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {...@@ -1885,6 +1885,46 @@ pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
1885 module.dump();1885 module.dump();
1886}1886}
18871887
1888/// For debugging purposes, prints a function representation to stderr.
1889pub 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
1888const EmitZIR = struct {1928const EmitZIR = struct {
1889 allocator: *Allocator,1929 allocator: *Allocator,
1890 arena: std.heap.ArenaAllocator,1930 arena: std.heap.ArenaAllocator,
...@@ -2065,6 +2105,36 @@ const EmitZIR = struct {...@@ -2065,6 +2105,36 @@ const EmitZIR = struct {
2065 return &declref_inst.base;2105 return &declref_inst.base;
2066 }2106 }
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
2068 fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {2138 fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
2069 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);2139 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
2070 defer inst_table.deinit();2140 defer inst_table.deinit();
src/zir_sema.zig+111-4
...@@ -25,6 +25,8 @@ const trace = @import("tracy.zig").trace;...@@ -25,6 +25,8 @@ const trace = @import("tracy.zig").trace;
25const Scope = Module.Scope;25const Scope = Module.Scope;
26const InnerError = Module.InnerError;26const InnerError = Module.InnerError;
27const Decl = Module.Decl;27const Decl = Module.Decl;
28const astgen = @import("astgen.zig");
29const ast = std.zig.ast;
2830
29pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {31pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
30 switch (old_inst.tag) {32 switch (old_inst.tag) {
...@@ -826,7 +828,112 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -826,7 +828,112 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
826828
827 const ret_type = func.ty.fnReturnType();829 const ret_type = func.ty.fnReturnType();
828830
829 const b = try mod.requireRuntimeBlock(scope, inst.base.src);831 const b = try mod.requireFunctionBlock(scope, inst.base.src);
832 if (b.is_comptime) {
833 const fn_val = try mod.resolveConstValue(scope, func);
834 const module_fn = switch (fn_val.tag()) {
835 .function => fn_val.castTag(.function).?.data,
836 .extern_fn => return mod.fail(scope, inst.base.src, "comptime call of extern function", .{}),
837 else => unreachable,
838 };
839 const callee_decl = module_fn.owner_decl;
840 const callee_file_scope = callee_decl.getFileScope();
841 const tree = mod.getAstTree(callee_file_scope) catch |err| switch (err) {
842 error.OutOfMemory => return error.OutOfMemory,
843 error.AnalysisFail => return error.AnalysisFail,
844 // TODO: make sure this gets retried and not cached
845 else => return mod.fail(scope, inst.base.src, "failed to load {s}: {s}", .{
846 callee_file_scope.sub_file_path, @errorName(err),
847 }),
848 };
849 const ast_node = tree.root_node.decls()[callee_decl.src_index];
850 const fn_proto = ast_node.castTag(.FnProto).?;
851
852 var call_arena = std.heap.ArenaAllocator.init(mod.gpa);
853 defer call_arena.deinit();
854
855 var gen_scope: Scope.GenZIR = .{
856 .decl = callee_decl,
857 .arena = &call_arena.allocator,
858 .parent = callee_decl.scope,
859 };
860 defer gen_scope.instructions.deinit(mod.gpa);
861
862 // Add a const instruction for each parameter.
863 var params_scope = &gen_scope.base;
864 for (fn_proto.params()) |param, i| {
865 const name_token = param.name_token.?;
866 const src = tree.token_locs[name_token].start;
867 const param_name = try mod.identifierTokenString(scope, name_token);
868 const arg_val = try mod.resolveConstValue(scope, casted_args[i]);
869 const arg = try astgen.addZIRInstConst(mod, params_scope, src, .{
870 .ty = casted_args[i].ty,
871 .val = arg_val,
872 });
873 const sub_scope = try call_arena.allocator.create(Scope.LocalVal);
874 sub_scope.* = .{
875 .parent = params_scope,
876 .gen_zir = &gen_scope,
877 .name = param_name,
878 .inst = arg,
879 };
880 params_scope = &sub_scope.base;
881 }
882
883 const body_node = fn_proto.getBodyNode().?; // We handle extern functions above.
884 const body_block = body_node.cast(ast.Node.Block).?;
885
886 try astgen.blockExpr(mod, params_scope, body_block);
887
888 if (gen_scope.instructions.items.len == 0 or
889 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
890 {
891 const src = tree.token_locs[body_block.rbrace].start;
892 _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .returnvoid);
893 }
894
895 if (mod.comp.verbose_ir) {
896 zir.dumpZir(mod.gpa, "fn_body_callee", callee_decl.name, gen_scope.instructions.items) catch {};
897 }
898
899 // Analyze the ZIR.
900 var inner_block: Scope.Block = .{
901 .parent = null,
902 .func = module_fn,
903 .decl = callee_decl,
904 .instructions = .{},
905 .arena = &call_arena.allocator,
906 .is_comptime = true,
907 };
908 defer inner_block.instructions.deinit(mod.gpa);
909
910 // TODO make sure compile errors that happen from this analyzeBody are reported correctly
911 // and attach to the caller Decl not the callee.
912 try analyzeBody(mod, &inner_block.base, .{
913 .instructions = gen_scope.instructions.items,
914 });
915
916 if (mod.comp.verbose_ir) {
917 inner_block.dump(mod.*);
918 }
919
920 assert(inner_block.instructions.items.len == 1);
921 const only_inst = inner_block.instructions.items[0];
922 switch (only_inst.tag) {
923 .ret => {
924 const ret_inst = only_inst.castTag(.ret).?;
925 const operand = ret_inst.operand;
926 const callee_arena = scope.arena();
927 return mod.constInst(scope, inst.base.src, .{
928 .ty = try operand.ty.copy(callee_arena),
929 .val = try operand.value().?.copy(callee_arena),
930 });
931 },
932 .retvoid => return mod.constVoid(scope, inst.base.src),
933 else => unreachable,
934 }
935 }
936
830 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);937 return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
831}938}
832939
...@@ -1509,7 +1616,7 @@ fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErr...@@ -1509,7 +1616,7 @@ fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErr
1509 return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand});1616 return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand});
1510 },1617 },
1511 else => {1618 else => {
1512 // TODO user friendly error to string1619 // TODO: make sure this gets retried and not cached
1513 return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });1620 return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
1514 },1621 },
1515 };1622 };
...@@ -1912,12 +2019,12 @@ fn analyzeInstUnreachable(...@@ -1912,12 +2019,12 @@ fn analyzeInstUnreachable(
19122019
1913fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {2020fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1914 const operand = try resolveInst(mod, scope, inst.positionals.operand);2021 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1915 const b = try mod.requireRuntimeBlock(scope, inst.base.src);2022 const b = try mod.requireFunctionBlock(scope, inst.base.src);
1916 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);2023 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
1917}2024}
19182025
1919fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {2026fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
1920 const b = try mod.requireRuntimeBlock(scope, inst.base.src);2027 const b = try mod.requireFunctionBlock(scope, inst.base.src);
1921 if (b.func) |func| {2028 if (b.func) |func| {
1922 // Need to emit a compile error if returning void is not allowed.2029 // Need to emit a compile error if returning void is not allowed.
1923 const void_inst = try mod.constVoid(scope, inst.base.src);2030 const void_inst = try mod.constVoid(scope, inst.base.src);
test/stage2/test.zig+24-1
...@@ -318,7 +318,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -318,7 +318,7 @@ pub fn addCases(ctx: *TestContext) !void {
318 }318 }
319319
320 {320 {
321 var case = ctx.exe("adding numbers at runtime", linux_x64);321 var case = ctx.exe("adding numbers at runtime and comptime", linux_x64);
322 case.addCompareOutput(322 case.addCompareOutput(
323 \\export fn _start() noreturn {323 \\export fn _start() noreturn {
324 \\ add(3, 4);324 \\ add(3, 4);
...@@ -342,6 +342,29 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -342,6 +342,29 @@ pub fn addCases(ctx: *TestContext) !void {
342 ,342 ,
343 "",343 "",
344 );344 );
345 case.addCompareOutput(
346 \\export fn _start() noreturn {
347 \\ exit();
348 \\}
349 \\
350 \\fn add(a: u32, b: u32) u32 {
351 \\ return a + b;
352 \\}
353 \\
354 \\const x = add(3, 4);
355 \\
356 \\fn exit() noreturn {
357 \\ asm volatile ("syscall"
358 \\ :
359 \\ : [number] "{rax}" (231),
360 \\ [arg1] "{rdi}" (x - 7)
361 \\ : "rcx", "r11", "memory"
362 \\ );
363 \\ unreachable;
364 \\}
365 ,
366 "",
367 );
345 }368 }
346369
347 {370 {