| author | |
| committer | |
| log | fea8659b82ea1a785f933c58ba9d65ceb05a4094 |
| tree | 91d33abe8ae357bc5aecc06e9fd2423bd83a10a8 |
| parent | fb37c1b0912c65d72b82f32df8bc7e780ab1ad80 |
* 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 | } |
| 270 | 270 | ||
| 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 | }; |
| 780 | 790 | ||
| 781 | /// This is a temporary structure, references to it are valid only | 791 | /// 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(); |
| 993 | 1003 | ||
| 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).?; |
| 1000 | 1010 | ||
| 1001 | decl.analysis = .in_progress; | 1011 | decl.analysis = .in_progress; |
| 1002 | 1012 | ||
| ... | @@ -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 identifierTokenString | 1144 | 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 | } |
| 1498 | 1508 | ||
| 1499 | fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { | 1509 | pub 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(); |
| 1502 | 1512 | ||
| 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 |
| 1549 | 1557 | ||
| 1550 | // We may be analyzing it for the first time, or this may be | 1558 | // 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(); |
| 1554 | 1562 | ||
| 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()) | ||
| 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+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); |
| 555 | 555 | ||
| 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 |
| 843 | fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { | 843 | fn 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); |
| 847 | 847 | ||
| 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 |
| 864 | 864 | ||
| 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 | } |
| 869 | 869 | ||
| 870 | // analyzing the error set results in a decl ref, so we might need to dereference it | 870 | // 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. |
| 990 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { | 990 | fn 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 | } |
| 995 | 995 | ||
| 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 | |||
| 1016 | pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | 996 | pub 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; |
| 1019 | 999 | ||
| 1020 | const ident_name = try identifierTokenString(mod, scope, node.token); | 1000 | const ident_name = try mod.identifierTokenString(scope, node.token); |
| 1021 | 1001 | ||
| 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(); |
| 1937 | 1917 | ||
| 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 | } |
| 1887 | 1887 | ||
| 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 | |||
| 1888 | const EmitZIR = struct { | 1928 | const 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 | } |
| 2067 | 2107 | ||
| 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; |
| 25 | const Scope = Module.Scope; | 25 | const Scope = Module.Scope; |
| 26 | const InnerError = Module.InnerError; | 26 | const InnerError = Module.InnerError; |
| 27 | const Decl = Module.Decl; | 27 | const Decl = Module.Decl; |
| 28 | const astgen = @import("astgen.zig"); | ||
| 29 | const ast = std.zig.ast; | ||
| 28 | 30 | ||
| 29 | pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | 31 | pub 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 |
| 826 | 828 | ||
| 827 | const ret_type = func.ty.fnReturnType(); | 829 | const ret_type = func.ty.fnReturnType(); |
| 828 | 830 | ||
| 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 | } |
| 832 | 939 | ||
| ... | @@ -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 string | 1619 | // 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( |
| 1912 | 2019 | ||
| 1913 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 2020 | fn 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 | } |
| 1918 | 2025 | ||
| 1919 | fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 2026 | fn 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 | } |
| 319 | 319 | ||
| 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 | } |
| 346 | 369 | ||
| 347 | { | 370 | { |