From 3d0f4b90305bc1815ccc86613cb3da715e9b62c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Feb 2021 23:29:55 -0700 Subject: stage2: start reworking Module/astgen for memory layout changes This commit does not reach any particular milestone, it is work-in-progress towards getting things to build. There's a `@panic("TODO")` in translate-c that should be removed when working on translate-c stuff. --- src/Module.zig | 1484 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 876 insertions(+), 608 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index fa9722814e..bd47332d15 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -244,9 +244,9 @@ pub const Decl = struct { } pub fn src(self: Decl) usize { - const tree = self.container.file_scope.contents.tree; - const decl_node = tree.root_node.decls()[self.src_index]; - return tree.token_locs[decl_node.firstToken()].start; + const tree = &self.container.file_scope.tree; + const decl_node = tree.rootDecls()[self.src_index]; + return tree.tokens.items(.start)[tree.firstToken(decl_node)]; } pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { @@ -536,6 +536,12 @@ pub const Scope = struct { pub const File = struct { pub const base_tag: Tag = .file; base: Scope = Scope{ .tag = base_tag }, + status: enum { + never_loaded, + unloaded_success, + unloaded_parse_failure, + loaded_success, + }, /// Relative to the owning package's root_src_dir. /// Reference to external memory, not owned by File. @@ -544,16 +550,8 @@ pub const Scope = struct { unloaded: void, bytes: [:0]const u8, }, - contents: union { - not_available: void, - tree: *ast.Tree, - }, - status: enum { - never_loaded, - unloaded_success, - unloaded_parse_failure, - loaded_success, - }, + /// Whether this is populated or not depends on `status`. + tree: ast.Tree, /// Package that this file is a part of, managed externally. pkg: *Package, @@ -567,7 +565,7 @@ pub const Scope = struct { => {}, .loaded_success => { - self.contents.tree.deinit(); + self.tree.deinit(gpa); self.status = .unloaded_success; }, } @@ -905,7 +903,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { .unreferenced => false, }; - const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { + const type_changed = mod.astgenAndSemaDecl(decl) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => return error.AnalysisFail, else => { @@ -947,129 +945,69 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { } } -fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { +/// Returns `true` if the Decl type changed. +/// Returns `true` if this is the first time analyzing the Decl. +/// Returns `false` otherwise. +fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { const tracy = trace(@src()); defer tracy.end(); - const tree = try self.getAstTree(decl.container.file_scope); - const ast_node = tree.root_node.decls()[decl.src_index]; - switch (ast_node.tag) { - .FnProto => { - const fn_proto = ast_node.castTag(.FnProto).?; + const tree = try mod.getAstTree(decl.container.file_scope); + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const decl_node = tree.rootDecls()[decl.src_index]; + switch (node_tags[decl_node]) { + .fn_decl => { + const fn_proto = node_datas[decl_node].lhs; + const body = node_datas[decl_node].rhs; + switch (node_tags[fn_proto]) { + .fn_proto_simple => { + var params: [1]ast.Node.Index = undefined; + return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoSimple(¶ms, fn_proto)); + }, + .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoMulti(fn_proto)), + .fn_proto_one => { + var params: [1]ast.Node.Index = undefined; + return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoOne(¶ms, fn_proto)); + }, + .fn_proto => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProto(fn_proto)), + else => unreachable, + } + }, + .fn_proto_simple => { + var params: [1]ast.Node.Index = undefined; + return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoSimple(¶ms, decl_node)); + }, + .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoMulti(decl_node)), + .fn_proto_one => { + var params: [1]ast.Node.Index = undefined; + return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoOne(¶ms, decl_node)); + }, + .fn_proto => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProto(decl_node)), + .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.globalVarDecl(decl_node)), + .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.localVarDecl(decl_node)), + .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.simpleVarDecl(decl_node)), + .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.alignedVarDecl(decl_node)), + + .@"comptime" => { decl.analysis = .in_progress; - // This arena allocator's memory is discarded at the end of this function. It is used - // to determine the type of the function, and hence the type of the decl, which is needed - // to complete the Decl analysis. - var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); - defer fn_type_scope_arena.deinit(); - var fn_type_scope: Scope.GenZIR = .{ + // A comptime decl does not store any value so we can just deinit this arena after analysis is done. + var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); + defer analysis_arena.deinit(); + var gen_scope: Scope.GenZIR = .{ .decl = decl, - .arena = &fn_type_scope_arena.allocator, + .arena = &analysis_arena.allocator, .parent = &decl.container.base, }; - defer fn_type_scope.instructions.deinit(self.gpa); - - decl.is_pub = fn_proto.getVisibToken() != null; - - const param_decls = fn_proto.params(); - const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); - - const fn_src = tree.token_locs[fn_proto.fn_token].start; - const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); - const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; - for (param_decls) |param_decl, i| { - const param_type_node = switch (param_decl.param_type) { - .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), - .type_expr => |node| node, - }; - param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); - } - if (fn_proto.getVarArgsToken()) |var_args_token| { - return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{}); - } - if (fn_proto.getLibName()) |lib_name| blk: { - const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name.firstToken()), "\""); // TODO: call identifierTokenString - log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); - const target = self.comp.getTarget(); - if (target_util.is_libc_lib_name(target, lib_name_str)) { - if (!self.comp.bin_file.options.link_libc) { - return self.failNode( - &fn_type_scope.base, - lib_name, - "dependency on libc must be explicitly specified in the build command", - .{}, - ); - } - break :blk; - } - if (target_util.is_libcpp_lib_name(target, lib_name_str)) { - if (!self.comp.bin_file.options.link_libcpp) { - return self.failNode( - &fn_type_scope.base, - lib_name, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } - break :blk; - } - if (!target.isWasm() and !self.comp.bin_file.options.pic) { - return self.failNode( - &fn_type_scope.base, - lib_name, - "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", - .{ lib_name, lib_name }, - ); - } - self.comp.stage1AddLinkLib(lib_name_str) catch |err| { - return self.failNode( - &fn_type_scope.base, - lib_name, - "unable to add link lib '{s}': {s}", - .{ lib_name, @errorName(err) }, - ); - }; - } - if (fn_proto.getAlignExpr()) |align_expr| { - return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); - } - if (fn_proto.getSectionExpr()) |sect_expr| { - return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); - } - if (fn_proto.getCallconvExpr()) |callconv_expr| { - return self.failNode( - &fn_type_scope.base, - callconv_expr, - "TODO implement function calling convention expression", - .{}, - ); - } - const return_type_expr = switch (fn_proto.return_type) { - .Explicit => |node| node, - .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), - .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), - }; - - const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); - const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ - .return_type = return_type_inst, - .param_types = param_types, - }, .{}); + defer gen_scope.instructions.deinit(self.gpa); + _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; + zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; } - // We need the memory for the Type to go into the arena for the Decl - var decl_arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer decl_arena.deinit(); - const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - var inst_table = Scope.Block.InstTable.init(self.gpa); defer inst_table.deinit(); @@ -1082,426 +1020,561 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .owner_decl = decl, .src_decl = decl, .instructions = .{}, - .arena = &decl_arena.allocator, + .arena = &analysis_arena.allocator, .inlining = null, - .is_comptime = false, + .is_comptime = true, .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); - const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ - .instructions = fn_type_scope.instructions.items, + _ = try zir_sema.analyzeBody(self, &block_scope, .{ + .instructions = gen_scope.instructions.items, }); - const body_node = fn_proto.getBodyNode() orelse { - // Extern function. - var type_changed = true; - if (decl.typedValueManaged()) |tvm| { - type_changed = !tvm.typed_value.ty.eql(fn_type); - tvm.deinit(self.gpa); - } - const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); + decl.analysis = .complete; + decl.generation = self.generation; + return true; + }, + .UsingNamespace => @panic("TODO usingnamespace decl"), + else => unreachable, + } +} - decl_arena_state.* = decl_arena.state; - decl.typed_value = .{ - .most_recent = .{ - .typed_value = .{ .ty = fn_type, .val = fn_val }, - .arena = decl_arena_state, - }, - }; - decl.analysis = .complete; - decl.generation = self.generation; +fn astgenAndSemaFn( + mod: *Module, + decl: *Decl, + tree: ast.Tree, + body_node: ast.Node.Index, + fn_proto: ast.full.FnProto, +) !bool { + const tracy = trace(@src()); + defer tracy.end(); - try self.comp.bin_file.allocateDeclIndexes(decl); - try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + decl.analysis = .in_progress; - if (type_changed and self.emit_h != null) { - try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); - } + const token_starts = tree.tokens.items(.start); - return type_changed; - }; + // This arena allocator's memory is discarded at the end of this function. It is used + // to determine the type of the function, and hence the type of the decl, which is needed + // to complete the Decl analysis. + var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); + defer fn_type_scope_arena.deinit(); + var fn_type_scope: Scope.GenZIR = .{ + .decl = decl, + .arena = &fn_type_scope_arena.allocator, + .parent = &decl.container.base, + }; + defer fn_type_scope.instructions.deinit(self.gpa); - const new_func = try decl_arena.allocator.create(Fn); - const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); + decl.is_pub = fn_proto.visib_token != null; - const fn_zir: zir.Body = blk: { - // We put the ZIR inside the Decl arena. - var gen_scope: Scope.GenZIR = .{ - .decl = decl, - .arena = &decl_arena.allocator, - .parent = &decl.container.base, - }; - defer gen_scope.instructions.deinit(self.gpa); - - // We need an instruction for each parameter, and they must be first in the body. - try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); - var params_scope = &gen_scope.base; - for (fn_proto.params()) |param, i| { - const name_token = param.name_token.?; - const src = tree.token_locs[name_token].start; - const param_name = try self.identifierTokenString(&gen_scope.base, name_token); - const arg = try decl_arena.allocator.create(zir.Inst.Arg); - arg.* = .{ - .base = .{ - .tag = .arg, - .src = src, - }, - .positionals = .{ - .name = param_name, - }, - .kw_args = .{}, - }; - gen_scope.instructions.items[i] = &arg.base; - const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); - sub_scope.* = .{ - .parent = params_scope, - .gen_zir = &gen_scope, - .name = param_name, - .inst = &arg.base, - }; - params_scope = &sub_scope.base; + // The AST params array does not contain anytype and ... parameters. + // We must iterate to count how many param types to allocate. + const param_count = blk: { + var count: usize = 0; + var it = fn_proto.iterate(tree); + while (it.next()) |_| { + count += 1; + } + break :blk count; + }; + const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count); + const fn_src = token_starts[fn_proto.ast.fn_token]; + const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }); + const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; + + { + var param_type_i: usize = 0; + var it = fn_proto.iterate(tree); + while (it.next()) |param| : (param_type_i += 1) { + if (param.anytype_ellipsis3) |token| { + switch (token_tags[token]) { + .keyword_anytype => return self.failTok( + &fn_type_scope.base, + tok_i, + "TODO implement anytype parameter", + .{}, + ), + .ellipsis3 => return self.failTok( + &fn_type_scope.base, + token, + "TODO implement var args", + .{}, + ), + else => unreachable, } + } + const param_type_node = param.type_expr; + assert(param_type_node != 0); + param_types[param_type_i] = + try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); + } + assert(param_type_i == param_count); + } + if (fn_proto.lib_name) |lib_name| blk: { + // TODO call std.zig.parseStringLiteral + const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name), "\""); + log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); + const target = self.comp.getTarget(); + if (target_util.is_libc_lib_name(target, lib_name_str)) { + if (!self.comp.bin_file.options.link_libc) { + return self.failTok( + &fn_type_scope.base, + lib_name, + "dependency on libc must be explicitly specified in the build command", + .{}, + ); + } + break :blk; + } + if (target_util.is_libcpp_lib_name(target, lib_name_str)) { + if (!self.comp.bin_file.options.link_libcpp) { + return self.failTok( + &fn_type_scope.base, + lib_name, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); + } + break :blk; + } + if (!target.isWasm() and !self.comp.bin_file.options.pic) { + return self.failTok( + &fn_type_scope.base, + lib_name, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ); + } + self.comp.stage1AddLinkLib(lib_name_str) catch |err| { + return self.failTok( + &fn_type_scope.base, + lib_name, + "unable to add link lib '{s}': {s}", + .{ lib_name, @errorName(err) }, + ); + }; + } + if (fn_proto.ast.align_expr) |align_expr| { + return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); + } + if (fn_proto.ast.section_expr) |sect_expr| { + return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); + } + if (fn_proto.ast.callconv_expr) |callconv_expr| { + return self.failNode( + &fn_type_scope.base, + callconv_expr, + "TODO implement function calling convention expression", + .{}, + ); + } + const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; + if (token_tags[maybe_bang] == .bang) { + return self.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); + } + const return_type_inst = try astgen.expr( + self, + &fn_type_scope.base, + type_type_rl, + fn_proto.ast.return_type, + ); + const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ + .return_type = return_type_inst, + .param_types = param_types, + }, .{}); - const body_block = body_node.cast(ast.Node.Block).?; + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { + zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; + } - try astgen.blockExpr(self, params_scope, body_block); + // We need the memory for the Type to go into the arena for the Decl + var decl_arena = std.heap.ArenaAllocator.init(self.gpa); + errdefer decl_arena.deinit(); + const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - if (gen_scope.instructions.items.len == 0 or - !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) - { - const src = tree.token_locs[body_block.rbrace].start; - _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); - } + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; - } + var branch_quota: u32 = default_eval_branch_quota; - break :blk .{ - .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), - }; - }; + var block_scope: Scope.Block = .{ + .parent = null, + .inst_table = &inst_table, + .func = null, + .owner_decl = decl, + .src_decl = decl, + .instructions = .{}, + .arena = &decl_arena.allocator, + .inlining = null, + .is_comptime = false, + .branch_quota = &branch_quota, + }; + defer block_scope.instructions.deinit(self.gpa); - const is_inline = blk: { - if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| { - if (tree.token_ids[maybe_inline_token] == .Keyword_inline) { - break :blk true; - } - } - break :blk false; - }; - const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; + const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ + .instructions = fn_type_scope.instructions.items, + }); + if (body_node == 0) { + // Extern function. + var type_changed = true; + if (decl.typedValueManaged()) |tvm| { + type_changed = !tvm.typed_value.ty.eql(fn_type); - new_func.* = .{ - .state = anal_state, - .zir = fn_zir, - .body = undefined, - .owner_decl = decl, - }; - fn_payload.* = .{ - .base = .{ .tag = .function }, - .data = new_func, - }; + tvm.deinit(self.gpa); + } + const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); - var prev_type_has_bits = false; - var prev_is_inline = false; - var type_changed = true; + decl_arena_state.* = decl_arena.state; + decl.typed_value = .{ + .most_recent = .{ + .typed_value = .{ .ty = fn_type, .val = fn_val }, + .arena = decl_arena_state, + }, + }; + decl.analysis = .complete; + decl.generation = self.generation; - if (decl.typedValueManaged()) |tvm| { - prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); - type_changed = !tvm.typed_value.ty.eql(fn_type); - if (tvm.typed_value.val.castTag(.function)) |payload| { - const prev_func = payload.data; - prev_is_inline = prev_func.state == .inline_only; - } + try self.comp.bin_file.allocateDeclIndexes(decl); + try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - tvm.deinit(self.gpa); - } + if (type_changed and self.emit_h != null) { + try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + } - decl_arena_state.* = decl_arena.state; - decl.typed_value = .{ - .most_recent = .{ - .typed_value = .{ - .ty = fn_type, - .val = Value.initPayload(&fn_payload.base), - }, - .arena = decl_arena_state, + return type_changed; + } + + const new_func = try decl_arena.allocator.create(Fn); + const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); + + const fn_zir: zir.Body = blk: { + // We put the ZIR inside the Decl arena. + var gen_scope: Scope.GenZIR = .{ + .decl = decl, + .arena = &decl_arena.allocator, + .parent = &decl.container.base, + }; + defer gen_scope.instructions.deinit(self.gpa); + + // We need an instruction for each parameter, and they must be first in the body. + try gen_scope.instructions.resize(self.gpa, param_count); + var params_scope = &gen_scope.base; + var i: usize = 0; + var it = fn_proto.iterate(tree); + while (it.next()) |param| : (i += 1) { + const name_token = param.name_token.?; + const src = token_starts[name_token]; + const param_name = try self.identifierTokenString(&gen_scope.base, name_token); + const arg = try decl_arena.allocator.create(zir.Inst.NoOp); + arg.* = .{ + .base = .{ + .tag = .arg, + .src = src, }, + .positionals = .{}, + .kw_args = .{}, }; - decl.analysis = .complete; - decl.generation = self.generation; + gen_scope.instructions.items[i] = &arg.base; + const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); + sub_scope.* = .{ + .parent = params_scope, + .gen_zir = &gen_scope, + .name = param_name, + .inst = &arg.base, + }; + params_scope = &sub_scope.base; + } - if (!is_inline and fn_type.hasCodeGenBits()) { - // We don't fully codegen the decl until later, but we do need to reserve a global - // offset table index for it. This allows us to codegen decls out of dependency order, - // increasing how many computations can be done in parallel. - try self.comp.bin_file.allocateDeclIndexes(decl); - try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - if (type_changed and self.emit_h != null) { - try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); - } - } else if (!prev_is_inline and prev_type_has_bits) { - self.comp.bin_file.freeDecl(decl); - } + try astgen.blockExpr(self, params_scope, body_node); - if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { - if (is_inline) { - return self.failTok( - &block_scope.base, - maybe_export_token, - "export of inline function", - .{}, - ); - } - const export_src = tree.token_locs[maybe_export_token].start; - const name_loc = tree.token_locs[fn_proto.getNameToken().?]; - const name = tree.tokenSliceLoc(name_loc); - // The scope needs to have the decl in it. - try self.analyzeExport(&block_scope.base, export_src, name, decl); - } - } - return type_changed or is_inline != prev_is_inline; - }, - .VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); + if (gen_scope.instructions.items.len == 0 or + !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) + { + const src = token_starts[tree.lastToken(body_node)]; + _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); + } - decl.analysis = .in_progress; + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { + zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; + } - // We need the memory for the Type to go into the arena for the Decl - var decl_arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer decl_arena.deinit(); - const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + break :blk .{ + .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), + }; + }; - var decl_inst_table = Scope.Block.InstTable.init(self.gpa); - defer decl_inst_table.deinit(); + const is_inline = fn_type.fnCallingConvention() == .Inline; + const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued; - var branch_quota: u32 = default_eval_branch_quota; + new_func.* = .{ + .state = anal_state, + .zir = fn_zir, + .body = undefined, + .owner_decl = decl, + }; + fn_payload.* = .{ + .base = .{ .tag = .function }, + .data = new_func, + }; - var block_scope: Scope.Block = .{ - .parent = null, - .inst_table = &decl_inst_table, - .func = null, - .owner_decl = decl, - .src_decl = decl, - .instructions = .{}, - .arena = &decl_arena.allocator, - .inlining = null, - .is_comptime = true, - .branch_quota = &branch_quota, - }; - defer block_scope.instructions.deinit(self.gpa); + var prev_type_has_bits = false; + var prev_is_inline = false; + var type_changed = true; - decl.is_pub = var_decl.getVisibToken() != null; - const is_extern = blk: { - const maybe_extern_token = var_decl.getExternExportToken() orelse - break :blk false; - if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; - if (var_decl.getInitNode()) |some| { - return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); - } - break :blk true; - }; - if (var_decl.getLibName()) |lib_name| { - assert(is_extern); - return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); - } - const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; - const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { - if (!is_mutable) { - return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); - } - break :blk true; - } else false; - assert(var_decl.getComptimeToken() == null); - if (var_decl.getAlignNode()) |align_expr| { - return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); - } - if (var_decl.getSectionNode()) |sect_expr| { - return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); - } + if (decl.typedValueManaged()) |tvm| { + prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); + type_changed = !tvm.typed_value.ty.eql(fn_type); + if (tvm.typed_value.val.castTag(.function)) |payload| { + const prev_func = payload.data; + prev_is_inline = prev_func.state == .inline_only; + } - const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { - var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); - defer gen_scope_arena.deinit(); - var gen_scope: Scope.GenZIR = .{ - .decl = decl, - .arena = &gen_scope_arena.allocator, - .parent = &decl.container.base, - }; - defer gen_scope.instructions.deinit(self.gpa); + tvm.deinit(self.gpa); + } - const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { - const src = tree.token_locs[type_node.firstToken()].start; - const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); - const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); - break :rl .{ .ty = var_type }; - } else .none; + decl_arena_state.* = decl_arena.state; + decl.typed_value = .{ + .most_recent = .{ + .typed_value = .{ + .ty = fn_type, + .val = Value.initPayload(&fn_payload.base), + }, + .arena = decl_arena_state, + }, + }; + decl.analysis = .complete; + decl.generation = self.generation; + + if (!is_inline and fn_type.hasCodeGenBits()) { + // We don't fully codegen the decl until later, but we do need to reserve a global + // offset table index for it. This allows us to codegen decls out of dependency order, + // increasing how many computations can be done in parallel. + try self.comp.bin_file.allocateDeclIndexes(decl); + try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + if (type_changed and self.emit_h != null) { + try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + } + } else if (!prev_is_inline and prev_type_has_bits) { + self.comp.bin_file.freeDecl(decl); + } - const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; - } + if (fn_proto.extern_export_token) |maybe_export_token| { + if (token_tags[maybe_export_token] == .Keyword_export) { + if (is_inline) { + return self.failTok( + &block_scope.base, + maybe_export_token, + "export of inline function", + .{}, + ); + } + const export_src = token_starts[maybe_export_token]; + const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString + // The scope needs to have the decl in it. + try self.analyzeExport(&block_scope.base, export_src, name, decl); + } + } + return type_changed or is_inline != prev_is_inline; +} - var var_inst_table = Scope.Block.InstTable.init(self.gpa); - defer var_inst_table.deinit(); - - var branch_quota_vi: u32 = default_eval_branch_quota; - var inner_block: Scope.Block = .{ - .parent = null, - .inst_table = &var_inst_table, - .func = null, - .owner_decl = decl, - .src_decl = decl, - .instructions = .{}, - .arena = &gen_scope_arena.allocator, - .inlining = null, - .is_comptime = true, - .branch_quota = &branch_quota_vi, - }; - defer inner_block.instructions.deinit(self.gpa); - try zir_sema.analyzeBody(self, &inner_block, .{ - .instructions = gen_scope.instructions.items, - }); +fn astgenAndSemaVarDecl( + mod: *Module, + decl: *Decl, + tree: ast.Tree, + var_decl: ast.full.VarDecl, +) !bool { + const tracy = trace(@src()); + defer tracy.end(); - // The result location guarantees the type coercion. - const analyzed_init_inst = var_inst_table.get(init_inst).?; - // The is_comptime in the Scope.Block guarantees the result is comptime-known. - const val = analyzed_init_inst.value().?; + decl.analysis = .in_progress; - const ty = try analyzed_init_inst.ty.copy(block_scope.arena); - break :vi .{ - .ty = ty, - .val = try val.copy(block_scope.arena), - }; - } else if (!is_extern) { - return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); - } else if (var_decl.getTypeNode()) |type_node| vi: { - // Temporary arena for the zir instructions. - var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); - defer type_scope_arena.deinit(); - var type_scope: Scope.GenZIR = .{ - .decl = decl, - .arena = &type_scope_arena.allocator, - .parent = &decl.container.base, - }; - defer type_scope.instructions.deinit(self.gpa); + const token_starts = tree.tokens.items(.start); - const var_type = try astgen.typeExpr(self, &type_scope.base, type_node); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; - } + // We need the memory for the Type to go into the arena for the Decl + var decl_arena = std.heap.ArenaAllocator.init(self.gpa); + errdefer decl_arena.deinit(); + const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ - .instructions = type_scope.instructions.items, - }); - break :vi .{ - .ty = ty, - .val = null, - }; - } else { - return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); - }; + var decl_inst_table = Scope.Block.InstTable.init(self.gpa); + defer decl_inst_table.deinit(); - if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { - return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); - } + var branch_quota: u32 = default_eval_branch_quota; - var type_changed = true; - if (decl.typedValueManaged()) |tvm| { - type_changed = !tvm.typed_value.ty.eql(var_info.ty); + var block_scope: Scope.Block = .{ + .parent = null, + .inst_table = &decl_inst_table, + .func = null, + .owner_decl = decl, + .src_decl = decl, + .instructions = .{}, + .arena = &decl_arena.allocator, + .inlining = null, + .is_comptime = true, + .branch_quota = &branch_quota, + }; + defer block_scope.instructions.deinit(self.gpa); + + decl.is_pub = var_decl.getVisibToken() != null; + const is_extern = blk: { + const maybe_extern_token = var_decl.getExternExportToken() orelse + break :blk false; + if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; + if (var_decl.getInitNode()) |some| { + return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); + } + break :blk true; + }; + if (var_decl.getLibName()) |lib_name| { + assert(is_extern); + return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); + } + const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; + const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { + if (!is_mutable) { + return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); + } + break :blk true; + } else false; + assert(var_decl.getComptimeToken() == null); + if (var_decl.getAlignNode()) |align_expr| { + return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); + } + if (var_decl.getSectionNode()) |sect_expr| { + return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); + } + + const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { + var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); + defer gen_scope_arena.deinit(); + var gen_scope: Scope.GenZIR = .{ + .decl = decl, + .arena = &gen_scope_arena.allocator, + .parent = &decl.container.base, + }; + defer gen_scope.instructions.deinit(self.gpa); - tvm.deinit(self.gpa); - } + const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { + const src = token_starts[type_node.firstToken()]; + const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }); + const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); + break :rl .{ .ty = var_type }; + } else .none; - const new_variable = try decl_arena.allocator.create(Var); - new_variable.* = .{ - .owner_decl = decl, - .init = var_info.val orelse undefined, - .is_extern = is_extern, - .is_mutable = is_mutable, - .is_threadlocal = is_threadlocal, - }; - const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); - - decl_arena_state.* = decl_arena.state; - decl.typed_value = .{ - .most_recent = .{ - .typed_value = .{ - .ty = var_info.ty, - .val = var_val, - }, - .arena = decl_arena_state, - }, - }; - decl.analysis = .complete; - decl.generation = self.generation; + const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node); + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { + zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; + } - if (var_decl.getExternExportToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { - const export_src = tree.token_locs[maybe_export_token].start; - const name_loc = tree.token_locs[var_decl.name_token]; - const name = tree.tokenSliceLoc(name_loc); - // The scope needs to have the decl in it. - try self.analyzeExport(&block_scope.base, export_src, name, decl); - } - } - return type_changed; - }, - .Comptime => { - const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node); + var var_inst_table = Scope.Block.InstTable.init(self.gpa); + defer var_inst_table.deinit(); + + var branch_quota_vi: u32 = default_eval_branch_quota; + var inner_block: Scope.Block = .{ + .parent = null, + .inst_table = &var_inst_table, + .func = null, + .owner_decl = decl, + .src_decl = decl, + .instructions = .{}, + .arena = &gen_scope_arena.allocator, + .inlining = null, + .is_comptime = true, + .branch_quota = &branch_quota_vi, + }; + defer inner_block.instructions.deinit(self.gpa); + try zir_sema.analyzeBody(self, &inner_block, .{ + .instructions = gen_scope.instructions.items, + }); - decl.analysis = .in_progress; + // The result location guarantees the type coercion. + const analyzed_init_inst = var_inst_table.get(init_inst).?; + // The is_comptime in the Scope.Block guarantees the result is comptime-known. + const val = analyzed_init_inst.value().?; - // A comptime decl does not store any value so we can just deinit this arena after analysis is done. - var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); - defer analysis_arena.deinit(); - var gen_scope: Scope.GenZIR = .{ - .decl = decl, - .arena = &analysis_arena.allocator, - .parent = &decl.container.base, - }; - defer gen_scope.instructions.deinit(self.gpa); + const ty = try analyzed_init_inst.ty.copy(block_scope.arena); + break :vi .{ + .ty = ty, + .val = try val.copy(block_scope.arena), + }; + } else if (!is_extern) { + return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); + } else if (var_decl.getTypeNode()) |type_node| vi: { + // Temporary arena for the zir instructions. + var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); + defer type_scope_arena.deinit(); + var type_scope: Scope.GenZIR = .{ + .decl = decl, + .arena = &type_scope_arena.allocator, + .parent = &decl.container.base, + }; + defer type_scope.instructions.deinit(self.gpa); - _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; - } + const var_type = try astgen.typeExpr(self, &type_scope.base, type_node); + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { + zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; + } - var inst_table = Scope.Block.InstTable.init(self.gpa); - defer inst_table.deinit(); + const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ + .instructions = type_scope.instructions.items, + }); + break :vi .{ + .ty = ty, + .val = null, + }; + } else { + return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); + }; - var branch_quota: u32 = default_eval_branch_quota; + if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { + return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); + } - var block_scope: Scope.Block = .{ - .parent = null, - .inst_table = &inst_table, - .func = null, - .owner_decl = decl, - .src_decl = decl, - .instructions = .{}, - .arena = &analysis_arena.allocator, - .inlining = null, - .is_comptime = true, - .branch_quota = &branch_quota, - }; - defer block_scope.instructions.deinit(self.gpa); + var type_changed = true; + if (decl.typedValueManaged()) |tvm| { + type_changed = !tvm.typed_value.ty.eql(var_info.ty); - _ = try zir_sema.analyzeBody(self, &block_scope, .{ - .instructions = gen_scope.instructions.items, - }); + tvm.deinit(self.gpa); + } - decl.analysis = .complete; - decl.generation = self.generation; - return true; + const new_variable = try decl_arena.allocator.create(Var); + new_variable.* = .{ + .owner_decl = decl, + .init = var_info.val orelse undefined, + .is_extern = is_extern, + .is_mutable = is_mutable, + .is_threadlocal = is_threadlocal, + }; + const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); + + decl_arena_state.* = decl_arena.state; + decl.typed_value = .{ + .most_recent = .{ + .typed_value = .{ + .ty = var_info.ty, + .val = var_val, + }, + .arena = decl_arena_state, }, - .Use => @panic("TODO usingnamespace decl"), - else => unreachable, + }; + decl.analysis = .complete; + decl.generation = self.generation; + + if (var_decl.getExternExportToken()) |maybe_export_token| { + if (tree.token_ids[maybe_export_token] == .Keyword_export) { + const export_src = token_starts[maybe_export_token]; + const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString + // The scope needs to have the decl in it. + try self.analyzeExport(&block_scope.base, export_src, name, decl); + } } + return type_changed; } fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { @@ -1512,7 +1585,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void dependee.dependants.putAssumeCapacity(depender, {}); } -pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { +pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree { const tracy = trace(@src()); defer tracy.end(); @@ -1523,8 +1596,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { const source = try root_scope.getSource(self); var keep_tree = false; - const tree = try std.zig.parse(self.gpa, source); - defer if (!keep_tree) tree.deinit(); + root_scope.tree = try std.zig.parse(self.gpa, source); + defer if (!keep_tree) root_scope.tree.deinit(self.gpa); + + const tree = &root_scope.tree; if (tree.errors.len != 0) { const parse_err = tree.errors[0]; @@ -1532,12 +1607,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { var msg = std.ArrayList(u8).init(self.gpa); defer msg.deinit(); - try parse_err.render(tree.token_ids, msg.writer()); + try tree.renderError(parse_err, msg.writer()); const err_msg = try self.gpa.create(ErrorMsg); err_msg.* = .{ .src_loc = .{ .file_scope = root_scope, - .byte_offset = tree.token_locs[parse_err.loc()].start, + .byte_offset = tree.tokens.items(.start)[parse_err.loc()], }, .msg = msg.toOwnedSlice(), }; @@ -1548,7 +1623,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { } root_scope.status = .loaded_success; - root_scope.contents = .{ .tree = tree }; keep_tree = true; return tree; @@ -1556,144 +1630,336 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { .unloaded_parse_failure => return error.AnalysisFail, - .loaded_success => return root_scope.contents.tree, + .loaded_success => return &root_scope.tree, } } -pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { +pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { const tracy = trace(@src()); defer tracy.end(); // We may be analyzing it for the first time, or this may be // an incremental update. This code handles both cases. - const tree = try self.getAstTree(container_scope.file_scope); - const decls = tree.root_node.decls(); + const tree = try mod.getAstTree(container_scope.file_scope); + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const decls = tree.rootDecls(); - try self.comp.work_queue.ensureUnusedCapacity(decls.len); - try container_scope.decls.ensureCapacity(self.gpa, decls.len); + try mod.comp.work_queue.ensureUnusedCapacity(decls.len); + try container_scope.decls.ensureCapacity(mod.gpa, decls.len); // Keep track of the decls that we expect to see in this file so that // we know which ones have been deleted. - var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); + var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa); defer deleted_decls.deinit(); try deleted_decls.ensureCapacity(container_scope.decls.items().len); for (container_scope.decls.items()) |entry| { deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); } - for (decls) |src_decl, decl_i| { - if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { - // We will create a Decl for it regardless of analysis status. - const name_tok = fn_proto.getNameToken() orelse { - @panic("TODO missing function name"); - }; - - const name_loc = tree.token_locs[name_tok]; - const name = tree.tokenSliceLoc(name_loc); - const name_hash = container_scope.fullyQualifiedNameHash(name); - const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); - if (self.decl_table.get(name_hash)) |decl| { - // Update the AST Node index of the decl, even if its contents are unchanged, it may - // have been re-ordered. - decl.src_index = decl_i; - if (deleted_decls.swapRemove(decl) == null) { - decl.analysis = .sema_failure; - const msg = try ErrorMsg.create(self.gpa, .{ - .file_scope = container_scope.file_scope, - .byte_offset = tree.token_locs[name_tok].start, - }, "redefinition of '{s}'", .{decl.name}); - errdefer msg.destroy(self.gpa); - try self.failed_decls.putNoClobber(self.gpa, decl, msg); - } else { - if (!srcHashEql(decl.contents_hash, contents_hash)) { - try self.markOutdatedDecl(decl); - decl.contents_hash = contents_hash; - } else switch (self.comp.bin_file.tag) { - .coff => { - // TODO Implement for COFF - }, - .elf => if (decl.fn_link.elf.len != 0) { - // TODO Look into detecting when this would be unnecessary by storing enough state - // in `Decl` to notice that the line number did not change. - self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); - }, - .macho => if (decl.fn_link.macho.len != 0) { - // TODO Look into detecting when this would be unnecessary by storing enough state - // in `Decl` to notice that the line number did not change. - self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); - }, - .c, .wasm => {}, - } - } - } else { - const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); - container_scope.decls.putAssumeCapacity(new_decl, {}); - if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { - self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); - } - } - } - } else if (src_decl.castTag(.VarDecl)) |var_decl| { - const name_loc = tree.token_locs[var_decl.name_token]; - const name = tree.tokenSliceLoc(name_loc); - const name_hash = container_scope.fullyQualifiedNameHash(name); - const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); - if (self.decl_table.get(name_hash)) |decl| { - // Update the AST Node index of the decl, even if its contents are unchanged, it may - // have been re-ordered. - decl.src_index = decl_i; - if (deleted_decls.swapRemove(decl) == null) { - decl.analysis = .sema_failure; - const err_msg = try ErrorMsg.create(self.gpa, .{ - .file_scope = container_scope.file_scope, - .byte_offset = name_loc.start, - }, "redefinition of '{s}'", .{decl.name}); - errdefer err_msg.destroy(self.gpa); - try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); - } else if (!srcHashEql(decl.contents_hash, contents_hash)) { - try self.markOutdatedDecl(decl); - decl.contents_hash = contents_hash; - } - } else { - const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); - container_scope.decls.putAssumeCapacity(new_decl, {}); - if (var_decl.getExternExportToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { - self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); - } - } + for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) { + .fn_decl => { + const fn_proto = node_datas[decl_node].lhs; + const body = node_datas[decl_node].rhs; + switch (node_tags[fn_proto]) { + .fn_proto_simple => { + var params: [1]ast.Node.Index = undefined; + try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + body, + tree.fnProtoSimple(¶ms, fn_proto), + ); + }, + .fn_proto_multi => try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + body, + tree.fnProtoMulti(fn_proto), + ), + .fn_proto_one => { + var params: [1]ast.Node.Index = undefined; + try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + body, + tree.fnProtoOne(¶ms, fn_proto), + ); + }, + .fn_proto => try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + body, + tree.fnProto(fn_proto), + ), + else => unreachable, } - } else if (src_decl.castTag(.Comptime)) |comptime_node| { - const name_index = self.getNextAnonNameIndex(); - const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index}); - defer self.gpa.free(name); + }, + .fn_proto_simple => { + var params: [1]ast.Node.Index = undefined; + try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + null, + tree.fnProtoSimple(¶ms, decl_node), + ); + }, + .fn_proto_multi => try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + null, + tree.fnProtoMulti(decl_node), + ), + .fn_proto_one => { + var params: [1]ast.Node.Index = undefined; + try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + null, + tree.fnProtoOne(¶ms, decl_node), + ); + }, + .fn_proto => try mod.semaContainerFn( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + null, + tree.fnProto(decl_node), + ), + + .global_var_decl => try mod.semaContainerVar( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.globalVarDecl(decl_node), + ), + .local_var_decl => try mod.semaContainerVar( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.localVarDecl(decl_node), + ), + .simple_var_decl => try mod.semaContainerVar( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.simpleVarDecl(decl_node), + ), + .aligned_var_decl => try mod.semaContainerVar( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.alignedVarDecl(decl_node), + ), + + .@"comptime" => { + const name_index = mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index}); + defer mod.gpa.free(name); const name_hash = container_scope.fullyQualifiedNameHash(name); - const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); + const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); - const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); + const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); container_scope.decls.putAssumeCapacity(new_decl, {}); - self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); - } else if (src_decl.castTag(.ContainerField)) |container_field| { - log.err("TODO: analyze container field", .{}); - } else if (src_decl.castTag(.TestDecl)) |test_decl| { + mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + }, + + .container_field_init => try mod.semaContainerField( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.containerFieldInit(decl), + ), + .container_field_align => try mod.semaContainerField( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.containerFieldAlign(decl), + ), + .container_field => try mod.semaContainerField( + container_scope, + &deleted_decls, + decl_node, + decl_i, + tree.*, + tree.containerField(decl), + ), + + .test_decl => { log.err("TODO: analyze test decl", .{}); - } else if (src_decl.castTag(.Use)) |use_decl| { + }, + .@"usingnamespace" => { log.err("TODO: analyze usingnamespace decl", .{}); - } else { - unreachable; - } - } + }, + else => unreachable, + }; // Handle explicitly deleted decls from the source code. Not to be confused // with when we delete decls because they are no longer referenced. for (deleted_decls.items()) |entry| { log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); - try self.deleteDecl(entry.key); + try mod.deleteDecl(entry.key); + } +} + +fn semaContainerFn( + mod: *Module, + container_scope: *Scope.Container, + deleted_decls: *std.AutoArrayHashMap(*Decl, void), + decl_node: ast.Node.Index, + decl_i: usize, + tree: ast.Tree, + body_node: ast.Node.Index, + fn_proto: ast.full.FnProto, +) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); + + // We will create a Decl for it regardless of analysis status. + const name_tok = fn_proto.name_token orelse { + @panic("TODO missing function name"); + }; + const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString + const name_hash = container_scope.fullyQualifiedNameHash(name); + const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); + if (mod.decl_table.get(name_hash)) |decl| { + // Update the AST Node index of the decl, even if its contents are unchanged, it may + // have been re-ordered. + decl.src_index = decl_i; + if (deleted_decls.swapRemove(decl) == null) { + decl.analysis = .sema_failure; + const msg = try ErrorMsg.create(mod.gpa, .{ + .file_scope = container_scope.file_scope, + .byte_offset = token_starts[name_tok], + }, "redefinition of '{s}'", .{decl.name}); + errdefer msg.destroy(mod.gpa); + try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); + } else { + if (!srcHashEql(decl.contents_hash, contents_hash)) { + try mod.markOutdatedDecl(decl); + decl.contents_hash = contents_hash; + } else switch (mod.comp.bin_file.tag) { + .coff => { + // TODO Implement for COFF + }, + .elf => if (decl.fn_link.elf.len != 0) { + // TODO Look into detecting when this would be unnecessary by storing enough state + // in `Decl` to notice that the line number did not change. + mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); + }, + .macho => if (decl.fn_link.macho.len != 0) { + // TODO Look into detecting when this would be unnecessary by storing enough state + // in `Decl` to notice that the line number did not change. + mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); + }, + .c, .wasm => {}, + } + } + } else { + const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); + container_scope.decls.putAssumeCapacity(new_decl, {}); + if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { + if (tree.token_ids[maybe_export_token] == .Keyword_export) { + mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + } + } + } +} + +fn semaContainerVar( + mod: *Module, + container_scope: *Scope.Container, + deleted_decls: *std.AutoArrayHashMap(*Decl, void), + decl_node: ast.Node.Index, + decl_i: usize, + tree: ast.Tree, + var_decl: ast.full.VarDecl, +) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const token_starts = tree.tokens.items(.start); + + const name_src = token_starts[var_decl.name_token]; + const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString + const name_hash = container_scope.fullyQualifiedNameHash(name); + const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); + if (mod.decl_table.get(name_hash)) |decl| { + // Update the AST Node index of the decl, even if its contents are unchanged, it may + // have been re-ordered. + decl.src_index = decl_i; + if (deleted_decls.swapRemove(decl) == null) { + decl.analysis = .sema_failure; + const err_msg = try ErrorMsg.create(mod.gpa, .{ + .file_scope = container_scope.file_scope, + .byte_offset = name_src, + }, "redefinition of '{s}'", .{decl.name}); + errdefer err_msg.destroy(mod.gpa); + try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); + } else if (!srcHashEql(decl.contents_hash, contents_hash)) { + try mod.markOutdatedDecl(decl); + decl.contents_hash = contents_hash; + } + } else { + const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); + container_scope.decls.putAssumeCapacity(new_decl, {}); + if (var_decl.getExternExportToken()) |maybe_export_token| { + if (tree.token_ids[maybe_export_token] == .Keyword_export) { + mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + } + } } } +fn semaContainerField() void { + const tracy = trace(@src()); + defer tracy.end(); + + log.err("TODO: analyze container field", .{}); +} + pub fn deleteDecl(self: *Module, decl: *Decl) !void { + const tracy = trace(@src()); + defer tracy.end(); + try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); // Remove from the namespace it resides in. In the case of an anonymous Decl it will @@ -2338,15 +2604,16 @@ pub fn createContainerDecl( fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { // TODO add namespaces, generic function signatrues const tree = scope.tree(); - const base_name = switch (tree.token_ids[base_token]) { - .Keyword_struct => "struct", - .Keyword_enum => "enum", - .Keyword_union => "union", - .Keyword_opaque => "opaque", + const token_tags = tree.tokens.items(.tag); + const base_name = switch (token_tags[base_token]) { + .keyword_struct => "struct", + .keyword_enum => "enum", + .keyword_union => "union", + .keyword_opaque => "opaque", else => unreachable, }; - const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]); - return std.fmt.allocPrint(self.gpa, "{}:{}:{}", .{ base_name, loc.line, loc.column }); + const loc = tree.tokenLocation(0, base_token); + return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column }); } fn getNextAnonNameIndex(self: *Module) usize { @@ -3092,7 +3359,7 @@ pub fn failTok( comptime format: []const u8, args: anytype, ) InnerError { - const src = scope.tree().token_locs[token_index].start; + const src = scope.tree().tokens.items(.start)[token_index]; return self.fail(scope, src, format, args); } @@ -3103,7 +3370,7 @@ pub fn failNode( comptime format: []const u8, args: anytype, ) InnerError { - const src = scope.tree().token_locs[ast_node.firstToken()].start; + const src = scope.tree().tokens.items(.start)[ast_node.firstToken()]; return self.fail(scope, src, format, args); } @@ -3537,6 +3804,7 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void /// Identifier token -> String (allocated in scope.arena()) pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { const tree = scope.tree(); + const token_starts = tree.tokens.items(.start); const ident_name = tree.tokenSlice(token); if (mem.startsWith(u8, ident_name, "@")) { @@ -3545,7 +3813,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { error.InvalidCharacter => { const bad_byte = raw_string[bad_index]; - const src = tree.token_locs[token].start; + const src = token_starts[token]; return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); }, else => |e| return e, -- cgit v1.2.3 From 7630a5c566b106b6325a55f29eb1ed9e584d0949 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 12 Feb 2021 23:47:17 -0700 Subject: stage2: more progress towards Module/astgen building with new mem layout --- lib/std/zig/ast.zig | 2 + src/Module.zig | 362 +++++++++------- src/astgen.zig | 921 ++++++++++++++++++++++------------------ src/codegen.zig | 15 +- src/ir.zig | 1 + src/link/Elf.zig | 32 +- src/link/MachO/DebugSymbols.zig | 32 +- src/zir.zig | 21 +- 8 files changed, 791 insertions(+), 595 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index ab81c3415e..40541ea7c1 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -2834,10 +2834,12 @@ pub const Node = struct { /// `(lhs)`. main_token is the `(`; rhs is the token index of the `)`. grouped_expression, /// `@a(lhs, rhs)`. lhs and rhs may be omitted. + /// main_token is the builtin token. builtin_call_two, /// Same as builtin_call_two but there is known to be a trailing comma before the rparen. builtin_call_two_comma, /// `@a(b, c)`. `sub_list[lhs..rhs]`. + /// main_token is the builtin token. builtin_call, /// Same as builtin_call but there is known to be a trailing comma before the rparen. builtin_call_comma, diff --git a/src/Module.zig b/src/Module.zig index a1c2822732..2071ff671c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -428,14 +428,14 @@ pub const Scope = struct { } /// Asserts the scope is a child of a File and has an AST tree and returns the tree. - pub fn tree(self: *Scope) *ast.Tree { + pub fn tree(self: *Scope) *const ast.Tree { switch (self.tag) { - .file => return self.cast(File).?.contents.tree, - .block => return self.cast(Block).?.src_decl.container.file_scope.contents.tree, - .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.contents.tree, - .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.contents.tree, - .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.contents.tree, - .container => return self.cast(Container).?.file_scope.contents.tree, + .file => return self.cast(File).?.tree, + .block => return self.cast(Block).?.src_decl.container.file_scope.tree, + .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.tree, + .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, + .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, + .container => return self.cast(Container).?.file_scope.tree, } } @@ -1008,38 +1008,38 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { switch (node_tags[fn_proto]) { .fn_proto_simple => { var params: [1]ast.Node.Index = undefined; - return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoSimple(¶ms, fn_proto)); + return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoSimple(¶ms, fn_proto)); }, - .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoMulti(fn_proto)), + .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoMulti(fn_proto)), .fn_proto_one => { var params: [1]ast.Node.Index = undefined; - return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoOne(¶ms, fn_proto)); + return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoOne(¶ms, fn_proto)); }, - .fn_proto => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProto(fn_proto)), + .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProto(fn_proto)), else => unreachable, } }, .fn_proto_simple => { var params: [1]ast.Node.Index = undefined; - return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoSimple(¶ms, decl_node)); + return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoSimple(¶ms, decl_node)); }, - .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoMulti(decl_node)), + .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoMulti(decl_node)), .fn_proto_one => { var params: [1]ast.Node.Index = undefined; - return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoOne(¶ms, decl_node)); + return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoOne(¶ms, decl_node)); }, - .fn_proto => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProto(decl_node)), + .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProto(decl_node)), - .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.globalVarDecl(decl_node)), - .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.localVarDecl(decl_node)), - .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.simpleVarDecl(decl_node)), - .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.alignedVarDecl(decl_node)), + .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.globalVarDecl(decl_node)), + .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.localVarDecl(decl_node)), + .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.simpleVarDecl(decl_node)), + .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.alignedVarDecl(decl_node)), .@"comptime" => { decl.analysis = .in_progress; // A comptime decl does not store any value so we can just deinit this arena after analysis is done. - var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); + var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); defer analysis_arena.deinit(); var gen_scope: Scope.GenZIR = .{ .decl = decl, @@ -1047,14 +1047,15 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { .parent = &decl.container.base, .force_comptime = true, }; - defer gen_scope.instructions.deinit(self.gpa); + defer gen_scope.instructions.deinit(mod.gpa); - _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; + const block_expr = node_datas[decl_node].lhs; + _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr); + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + zir.dumpZir(mod.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; } - var inst_table = Scope.Block.InstTable.init(self.gpa); + var inst_table = Scope.Block.InstTable.init(mod.gpa); defer inst_table.deinit(); var branch_quota: u32 = default_eval_branch_quota; @@ -1071,17 +1072,17 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { .is_comptime = true, .branch_quota = &branch_quota, }; - defer block_scope.instructions.deinit(self.gpa); + defer block_scope.instructions.deinit(mod.gpa); - _ = try zir_sema.analyzeBody(self, &block_scope, .{ + _ = try zir_sema.analyzeBody(mod, &block_scope, .{ .instructions = gen_scope.instructions.items, }); decl.analysis = .complete; - decl.generation = self.generation; + decl.generation = mod.generation; return true; }, - .UsingNamespace => @panic("TODO usingnamespace decl"), + .@"usingnamespace" => @panic("TODO usingnamespace decl"), else => unreachable, } } @@ -1099,18 +1100,20 @@ fn astgenAndSemaFn( decl.analysis = .in_progress; const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); // This arena allocator's memory is discarded at the end of this function. It is used // to determine the type of the function, and hence the type of the decl, which is needed // to complete the Decl analysis. - var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); + var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); defer fn_type_scope_arena.deinit(); var fn_type_scope: Scope.GenZIR = .{ .decl = decl, .arena = &fn_type_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; - defer fn_type_scope.instructions.deinit(self.gpa); + defer fn_type_scope.instructions.deinit(mod.gpa); decl.is_pub = fn_proto.visib_token != null; @@ -1126,7 +1129,7 @@ fn astgenAndSemaFn( }; const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count); const fn_src = token_starts[fn_proto.ast.fn_token]; - const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ + const type_type = try astgen.addZIRInstConst(mod, &fn_type_scope.base, fn_src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type), }); @@ -1138,13 +1141,13 @@ fn astgenAndSemaFn( while (it.next()) |param| : (param_type_i += 1) { if (param.anytype_ellipsis3) |token| { switch (token_tags[token]) { - .keyword_anytype => return self.failTok( + .keyword_anytype => return mod.failTok( &fn_type_scope.base, - tok_i, + token, "TODO implement anytype parameter", .{}, ), - .ellipsis3 => return self.failTok( + .ellipsis3 => return mod.failTok( &fn_type_scope.base, token, "TODO implement var args", @@ -1156,7 +1159,7 @@ fn astgenAndSemaFn( const param_type_node = param.type_expr; assert(param_type_node != 0); param_types[param_type_i] = - try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); + try astgen.expr(mod, &fn_type_scope.base, type_type_rl, param_type_node); } assert(param_type_i == param_count); } @@ -1164,10 +1167,10 @@ fn astgenAndSemaFn( // TODO call std.zig.parseStringLiteral const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name), "\""); log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); - const target = self.comp.getTarget(); + const target = mod.comp.getTarget(); if (target_util.is_libc_lib_name(target, lib_name_str)) { - if (!self.comp.bin_file.options.link_libc) { - return self.failTok( + if (!mod.comp.bin_file.options.link_libc) { + return mod.failTok( &fn_type_scope.base, lib_name, "dependency on libc must be explicitly specified in the build command", @@ -1177,8 +1180,8 @@ fn astgenAndSemaFn( break :blk; } if (target_util.is_libcpp_lib_name(target, lib_name_str)) { - if (!self.comp.bin_file.options.link_libcpp) { - return self.failTok( + if (!mod.comp.bin_file.options.link_libcpp) { + return mod.failTok( &fn_type_scope.base, lib_name, "dependency on libc++ must be explicitly specified in the build command", @@ -1187,16 +1190,16 @@ fn astgenAndSemaFn( } break :blk; } - if (!target.isWasm() and !self.comp.bin_file.options.pic) { - return self.failTok( + if (!target.isWasm() and !mod.comp.bin_file.options.pic) { + return mod.failTok( &fn_type_scope.base, lib_name, "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", .{ lib_name, lib_name }, ); } - self.comp.stage1AddLinkLib(lib_name_str) catch |err| { - return self.failTok( + mod.comp.stage1AddLinkLib(lib_name_str) catch |err| { + return mod.failTok( &fn_type_scope.base, lib_name, "unable to add link lib '{s}': {s}", @@ -1204,45 +1207,55 @@ fn astgenAndSemaFn( ); }; } - if (fn_proto.ast.align_expr) |align_expr| { - return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); + if (fn_proto.ast.align_expr != 0) { + return mod.failNode( + &fn_type_scope.base, + fn_proto.ast.align_expr, + "TODO implement function align expression", + .{}, + ); } - if (fn_proto.ast.section_expr) |sect_expr| { - return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); + if (fn_proto.ast.section_expr != 0) { + return mod.failNode( + &fn_type_scope.base, + fn_proto.ast.section_expr, + "TODO implement function section expression", + .{}, + ); } - if (fn_proto.ast.callconv_expr) |callconv_expr| { - return self.failNode( + if (fn_proto.ast.callconv_expr != 0) { + return mod.failNode( &fn_type_scope.base, - callconv_expr, + fn_proto.ast.callconv_expr, "TODO implement function calling convention expression", .{}, ); } const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; if (token_tags[maybe_bang] == .bang) { - return self.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); + return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); } const return_type_inst = try astgen.expr( - self, + mod, &fn_type_scope.base, type_type_rl, fn_proto.ast.return_type, ); - const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ + const fn_type_inst = try astgen.addZIRInst(mod, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ .return_type = return_type_inst, .param_types = param_types, }, .{}); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; } // We need the memory for the Type to go into the arena for the Decl - var decl_arena = std.heap.ArenaAllocator.init(self.gpa); + var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - var inst_table = Scope.Block.InstTable.init(self.gpa); + var inst_table = Scope.Block.InstTable.init(mod.gpa); defer inst_table.deinit(); var branch_quota: u32 = default_eval_branch_quota; @@ -1259,9 +1272,9 @@ fn astgenAndSemaFn( .is_comptime = false, .branch_quota = &branch_quota, }; - defer block_scope.instructions.deinit(self.gpa); + defer block_scope.instructions.deinit(mod.gpa); - const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ + const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{ .instructions = fn_type_scope.instructions.items, }); if (body_node == 0) { @@ -1270,7 +1283,7 @@ fn astgenAndSemaFn( if (decl.typedValueManaged()) |tvm| { type_changed = !tvm.typed_value.ty.eql(fn_type); - tvm.deinit(self.gpa); + tvm.deinit(mod.gpa); } const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); @@ -1282,13 +1295,13 @@ fn astgenAndSemaFn( }, }; decl.analysis = .complete; - decl.generation = self.generation; + decl.generation = mod.generation; - try self.comp.bin_file.allocateDeclIndexes(decl); - try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + try mod.comp.bin_file.allocateDeclIndexes(decl); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - if (type_changed and self.emit_h != null) { - try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + if (type_changed and mod.emit_h != null) { + try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); } return type_changed; @@ -1304,17 +1317,17 @@ fn astgenAndSemaFn( .arena = &decl_arena.allocator, .parent = &decl.container.base, }; - defer gen_scope.instructions.deinit(self.gpa); + defer gen_scope.instructions.deinit(mod.gpa); // We need an instruction for each parameter, and they must be first in the body. - try gen_scope.instructions.resize(self.gpa, param_count); + try gen_scope.instructions.resize(mod.gpa, param_count); var params_scope = &gen_scope.base; var i: usize = 0; var it = fn_proto.iterate(tree); while (it.next()) |param| : (i += 1) { const name_token = param.name_token.?; const src = token_starts[name_token]; - const param_name = try self.identifierTokenString(&gen_scope.base, name_token); + const param_name = try mod.identifierTokenString(&gen_scope.base, name_token); const arg = try decl_arena.allocator.create(zir.Inst.NoOp); arg.* = .{ .base = .{ @@ -1335,17 +1348,17 @@ fn astgenAndSemaFn( params_scope = &sub_scope.base; } - try astgen.blockExpr(self, params_scope, body_node); + try astgen.blockExpr(mod, params_scope, body_node); if (gen_scope.instructions.items.len == 0 or !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) { const src = token_starts[tree.lastToken(body_node)]; - _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); + _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .returnvoid); } - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; } break :blk .{ @@ -1379,7 +1392,7 @@ fn astgenAndSemaFn( prev_is_inline = prev_func.state == .inline_only; } - tvm.deinit(self.gpa); + tvm.deinit(mod.gpa); } decl_arena_state.* = decl_arena.state; @@ -1393,25 +1406,25 @@ fn astgenAndSemaFn( }, }; decl.analysis = .complete; - decl.generation = self.generation; + decl.generation = mod.generation; if (!is_inline and fn_type.hasCodeGenBits()) { // We don't fully codegen the decl until later, but we do need to reserve a global // offset table index for it. This allows us to codegen decls out of dependency order, // increasing how many computations can be done in parallel. - try self.comp.bin_file.allocateDeclIndexes(decl); - try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - if (type_changed and self.emit_h != null) { - try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + try mod.comp.bin_file.allocateDeclIndexes(decl); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + if (type_changed and mod.emit_h != null) { + try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); } } else if (!prev_is_inline and prev_type_has_bits) { - self.comp.bin_file.freeDecl(decl); + mod.comp.bin_file.freeDecl(decl); } if (fn_proto.extern_export_token) |maybe_export_token| { - if (token_tags[maybe_export_token] == .Keyword_export) { + if (token_tags[maybe_export_token] == .keyword_export) { if (is_inline) { - return self.failTok( + return mod.failTok( &block_scope.base, maybe_export_token, "export of inline function", @@ -1421,7 +1434,7 @@ fn astgenAndSemaFn( const export_src = token_starts[maybe_export_token]; const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString // The scope needs to have the decl in it. - try self.analyzeExport(&block_scope.base, export_src, name, decl); + try mod.analyzeExport(&block_scope.base, export_src, name, decl); } } return type_changed or is_inline != prev_is_inline; @@ -1439,13 +1452,14 @@ fn astgenAndSemaVarDecl( decl.analysis = .in_progress; const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); // We need the memory for the Type to go into the arena for the Decl - var decl_arena = std.heap.ArenaAllocator.init(self.gpa); + var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - var decl_inst_table = Scope.Block.InstTable.init(self.gpa); + var decl_inst_table = Scope.Block.InstTable.init(mod.gpa); defer decl_inst_table.deinit(); var branch_quota: u32 = default_eval_branch_quota; @@ -1462,63 +1476,83 @@ fn astgenAndSemaVarDecl( .is_comptime = true, .branch_quota = &branch_quota, }; - defer block_scope.instructions.deinit(self.gpa); + defer block_scope.instructions.deinit(mod.gpa); - decl.is_pub = var_decl.getVisibToken() != null; + decl.is_pub = var_decl.visib_token != null; const is_extern = blk: { - const maybe_extern_token = var_decl.getExternExportToken() orelse - break :blk false; - if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; - if (var_decl.getInitNode()) |some| { - return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); + const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; + if (token_tags[maybe_extern_token] != .keyword_extern) break :blk false; + if (var_decl.ast.init_node != 0) { + return mod.failNode( + &block_scope.base, + var_decl.ast.init_node, + "extern variables have no initializers", + .{}, + ); } break :blk true; }; - if (var_decl.getLibName()) |lib_name| { + if (var_decl.lib_name) |lib_name| { assert(is_extern); - return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); + return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{}); } - const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; - const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { + const is_mutable = token_tags[var_decl.mut_token] == .keyword_var; + const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: { if (!is_mutable) { - return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); + return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); } break :blk true; } else false; - assert(var_decl.getComptimeToken() == null); - if (var_decl.getAlignNode()) |align_expr| { - return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); + assert(var_decl.comptime_token == null); + if (var_decl.ast.align_node != 0) { + return mod.failNode( + &block_scope.base, + var_decl.ast.align_node, + "TODO implement function align expression", + .{}, + ); } - if (var_decl.getSectionNode()) |sect_expr| { - return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); + if (var_decl.ast.section_node != 0) { + return mod.failNode( + &block_scope.base, + var_decl.ast.section_node, + "TODO implement function section expression", + .{}, + ); } - const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { - var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); + const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: { + var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); defer gen_scope_arena.deinit(); var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &gen_scope_arena.allocator, .parent = &decl.container.base, }; - defer gen_scope.instructions.deinit(self.gpa); + defer gen_scope.instructions.deinit(mod.gpa); - const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { - const src = token_starts[type_node.firstToken()]; - const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ + const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) rl: { + const type_node = var_decl.ast.type_node; + const src = token_starts[tree.firstToken(type_node)]; + const type_type = try astgen.addZIRInstConst(mod, &gen_scope.base, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type), }); - const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); + const var_type = try astgen.expr(mod, &gen_scope.base, .{ .ty = type_type }, type_node); break :rl .{ .ty = var_type }; } else .none; - const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; + const init_inst = try astgen.comptimeExpr( + mod, + &gen_scope.base, + init_result_loc, + var_decl.ast.init_node, + ); + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + zir.dumpZir(mod.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; } - var var_inst_table = Scope.Block.InstTable.init(self.gpa); + var var_inst_table = Scope.Block.InstTable.init(mod.gpa); defer var_inst_table.deinit(); var branch_quota_vi: u32 = default_eval_branch_quota; @@ -1534,8 +1568,8 @@ fn astgenAndSemaVarDecl( .is_comptime = true, .branch_quota = &branch_quota_vi, }; - defer inner_block.instructions.deinit(self.gpa); - try zir_sema.analyzeBody(self, &inner_block, .{ + defer inner_block.instructions.deinit(mod.gpa); + try zir_sema.analyzeBody(mod, &inner_block, .{ .instructions = gen_scope.instructions.items, }); @@ -1550,24 +1584,30 @@ fn astgenAndSemaVarDecl( .val = try val.copy(block_scope.arena), }; } else if (!is_extern) { - return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); - } else if (var_decl.getTypeNode()) |type_node| vi: { + return mod.failTok( + &block_scope.base, + tree.firstToken(var_decl), + "variables must be initialized", + .{}, + ); + } else if (var_decl.ast.type_node != 0) vi: { + const type_node = var_decl.ast.type_node; // Temporary arena for the zir instructions. - var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); + var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); defer type_scope_arena.deinit(); var type_scope: Scope.GenZIR = .{ .decl = decl, .arena = &type_scope_arena.allocator, .parent = &decl.container.base, }; - defer type_scope.instructions.deinit(self.gpa); + defer type_scope.instructions.deinit(mod.gpa); - const var_type = try astgen.typeExpr(self, &type_scope.base, type_node); - if (std.builtin.mode == .Debug and self.comp.verbose_ir) { - zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; + const var_type = try astgen.typeExpr(mod, &type_scope.base, type_node); + if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { + zir.dumpZir(mod.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; } - const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ + const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{ .instructions = type_scope.instructions.items, }); break :vi .{ @@ -1575,18 +1615,28 @@ fn astgenAndSemaVarDecl( .val = null, }; } else { - return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); + return mod.failTok( + &block_scope.base, + tree.firstToken(var_decl), + "unable to infer variable type", + .{}, + ); }; if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { - return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); + return mod.failTok( + &block_scope.base, + tree.firstToken(var_decl), + "variable of type '{}' must be const", + .{var_info.ty}, + ); } var type_changed = true; if (decl.typedValueManaged()) |tvm| { type_changed = !tvm.typed_value.ty.eql(var_info.ty); - tvm.deinit(self.gpa); + tvm.deinit(mod.gpa); } const new_variable = try decl_arena.allocator.create(Var); @@ -1610,14 +1660,15 @@ fn astgenAndSemaVarDecl( }, }; decl.analysis = .complete; - decl.generation = self.generation; + decl.generation = mod.generation; - if (var_decl.getExternExportToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { + if (var_decl.extern_export_token) |maybe_export_token| { + if (token_tags[maybe_export_token] == .keyword_export) { const export_src = token_starts[maybe_export_token]; - const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString + const name_token = var_decl.ast.mut_token + 1; + const name = tree.tokenSlice(name_token); // TODO identifierTokenString // The scope needs to have the decl in it. - try self.analyzeExport(&block_scope.base, export_src, name, decl); + try mod.analyzeExport(&block_scope.base, export_src, name, decl); } } return type_changed; @@ -1761,7 +1812,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - null, + 0, tree.fnProtoSimple(¶ms, decl_node), ); }, @@ -1771,7 +1822,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - null, + 0, tree.fnProtoMulti(decl_node), ), .fn_proto_one => { @@ -1782,7 +1833,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - null, + 0, tree.fnProtoOne(¶ms, decl_node), ); }, @@ -1792,7 +1843,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - null, + 0, tree.fnProto(decl_node), ), @@ -1848,7 +1899,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - tree.containerFieldInit(decl), + tree.containerFieldInit(decl_node), ), .container_field_align => try mod.semaContainerField( container_scope, @@ -1856,7 +1907,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - tree.containerFieldAlign(decl), + tree.containerFieldAlign(decl_node), ), .container_field => try mod.semaContainerField( container_scope, @@ -1864,7 +1915,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { decl_node, decl_i, tree.*, - tree.containerField(decl), + tree.containerField(decl_node), ), .test_decl => { @@ -1936,14 +1987,14 @@ fn semaContainerFn( // in `Decl` to notice that the line number did not change. mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } } else { const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); container_scope.decls.putAssumeCapacity(new_decl, {}); - if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { + if (fn_proto.extern_export_token) |maybe_export_token| { + if (token_tags[maybe_export_token] == .keyword_export) { mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); } } @@ -1963,9 +2014,11 @@ fn semaContainerVar( defer tracy.end(); const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); - const name_src = token_starts[var_decl.name_token]; - const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString + const name_token = var_decl.ast.mut_token + 1; + const name_src = token_starts[name_token]; + const name = tree.tokenSlice(name_token); // TODO identifierTokenString const name_hash = container_scope.fullyQualifiedNameHash(name); const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); if (mod.decl_table.get(name_hash)) |decl| { @@ -1987,15 +2040,23 @@ fn semaContainerVar( } else { const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); container_scope.decls.putAssumeCapacity(new_decl, {}); - if (var_decl.getExternExportToken()) |maybe_export_token| { - if (tree.token_ids[maybe_export_token] == .Keyword_export) { + if (var_decl.extern_export_token) |maybe_export_token| { + if (token_tags[maybe_export_token] == .keyword_export) { mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); } } } } -fn semaContainerField() void { +fn semaContainerField( + mod: *Module, + container_scope: *Scope.Container, + deleted_decls: *std.AutoArrayHashMap(*Decl, void), + decl_node: ast.Node.Index, + decl_i: usize, + tree: ast.Tree, + field: ast.full.ContainerField, +) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2898,7 +2959,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: [] file_scope.* = .{ .sub_file_path = resolved_path, .source = .{ .unloaded = {} }, - .contents = .{ .not_available = {} }, + .tree = undefined, .status = .never_loaded, .pkg = found_pkg orelse cur_pkg, .root_container = .{ @@ -3415,11 +3476,12 @@ pub fn failTok( pub fn failNode( self: *Module, scope: *Scope, - ast_node: *ast.Node, + ast_node: ast.Node.Index, comptime format: []const u8, args: anytype, ) InnerError { - const src = scope.tree().tokens.items(.start)[ast_node.firstToken()]; + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.firstToken(ast_node)]; return self.fail(scope, src, format, args); } diff --git a/src/astgen.zig b/src/astgen.zig index ece16d70da..dcc2ea9ad2 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -55,7 +55,7 @@ pub const ResultLoc = union(enum) { }; }; -pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { +pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!*zir.Inst { const type_src = scope.tree().token_locs[type_node.firstToken()].start; const type_type = try addZIRInstConst(mod, scope, type_src, .{ .ty = Type.initTag(.type), @@ -65,134 +65,133 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z return expr(mod, scope, type_rl, type_node); } -fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { - switch (node.tag) { - .Root => unreachable, - .Use => unreachable, - .TestDecl => unreachable, - .DocComment => unreachable, - .VarDecl => unreachable, - .SwitchCase => unreachable, - .SwitchElse => unreachable, - .Else => unreachable, - .Payload => unreachable, - .PointerPayload => unreachable, - .PointerIndexPayload => unreachable, - .ErrorTag => unreachable, - .FieldInitializer => unreachable, - .ContainerField => unreachable, - - .Assign, - .AssignBitAnd, - .AssignBitOr, - .AssignBitShiftLeft, - .AssignBitShiftRight, - .AssignBitXor, - .AssignDiv, - .AssignSub, - .AssignSubWrap, - .AssignMod, - .AssignAdd, - .AssignAddWrap, - .AssignMul, - .AssignMulWrap, - .Add, - .AddWrap, - .Sub, - .SubWrap, - .Mul, - .MulWrap, - .Div, - .Mod, - .BitAnd, - .BitOr, - .BitShiftLeft, - .BitShiftRight, - .BitXor, - .BangEqual, - .EqualEqual, - .GreaterThan, - .GreaterOrEqual, - .LessThan, - .LessOrEqual, - .ArrayCat, - .ArrayMult, - .BoolAnd, - .BoolOr, - .Asm, - .StringLiteral, - .IntegerLiteral, - .Call, - .Unreachable, - .Return, - .If, - .While, - .BoolNot, - .AddressOf, - .FloatLiteral, - .UndefinedLiteral, - .BoolLiteral, - .NullLiteral, - .OptionalType, - .Block, - .LabeledBlock, - .Break, +fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const node_tags = tree.nodes.items(.tag); + const main_tokens = tree.nodes.items(.main_token); + switch (node_tags[node]) { + .root => unreachable, + .@"usingnamespace" => unreachable, + .test_decl => unreachable, + .doc_comment => unreachable, + .var_decl => unreachable, + .switch_case => unreachable, + .switch_else => unreachable, + .container_field_init => unreachable, + .container_field_align => unreachable, + .container_field => unreachable, + + .assign, + .assign_bit_and, + .assign_bit_or, + .assign_bit_shift_left, + .assign_bit_shift_right, + .assign_bit_xor, + .assign_div, + .assign_sub, + .assign_sub_wrap, + .assign_mod, + .assign_add, + .assign_add_wrap, + .assign_mul, + .assign_mul_wrap, + .add, + .add_wrap, + .sub, + .sub_wrap, + .mul, + .mul_wrap, + .div, + .mod, + .bit_and, + .bit_or, + .bit_shift_left, + .bit_shift_right, + .bit_xor, + .bang_equal, + .equal_equal, + .greater_than, + .greater_or_equal, + .less_than, + .less_or_equal, + .array_cat, + .array_mult, + .bool_and, + .bool_or, + .@"asm", + .string_literal, + .integer_literal, + .call, + .@"unreachable", + .@"return", + .@"if", + .@"while", + .bool_not, + .address_of, + .float_literal, + .undefined_literal, + .bool_literal, + .null_literal, + .optional_type, + .block, + .labeled_block, + .@"break", .PtrType, - .ArrayType, - .ArrayTypeSentinel, - .EnumLiteral, + .array_type, + .array_type_sentinel, + .enum_literal, .MultilineStringLiteral, - .CharLiteral, - .Defer, - .Catch, - .ErrorUnion, - .MergeErrorSets, - .Range, - .Await, - .BitNot, - .Negation, - .NegationWrap, - .Resume, - .Try, - .SliceType, - .Slice, + .char_literal, + .@"defer", + .@"catch", + .error_union, + .merge_error_sets, + .range, + .@"await", + .bit_not, + .negation, + .negation_wrap, + .@"resume", + .@"try", + .slice_type, + .slice, .ArrayInitializer, .ArrayInitializerDot, .StructInitializer, .StructInitializerDot, - .Switch, - .For, - .Suspend, - .Continue, - .AnyType, - .ErrorType, + .@"switch", + .@"for", + .@"suspend", + .@"continue", + .@"anytype", + .error_type, .FnProto, - .AnyFrameType, - .ErrorSetDecl, + .anyframe_type, + .error_set_decl, .ContainerDecl, - .Comptime, - .Nosuspend, + .@"comptime", + .@"nosuspend", + .builtin_call, + .builtin_call_comma, => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), - // @field can be assigned to - .BuiltinCall => { - const call = node.castTag(.BuiltinCall).?; - const tree = scope.tree(); - const builtin_name = tree.tokenSlice(call.builtin_token); - + // `@field` can be assigned to. + .builtin_call_two, .builtin_call_two_comma => { + const builtin_token = main_tokens[node]; + const builtin_name = tree.tokenSlice(builtin_token); if (!mem.eql(u8, builtin_name, "@field")) { return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); } }, // can be assigned to - .UnwrapOptional, - .Deref, - .Period, - .ArrayAccess, - .Identifier, - .GroupedExpression, - .OrElse, + .unwrap_optional, + .deref, + .period, + .array_access, + .identifier, + .grouped_expression, + .@"orelse", => {}, } return expr(mod, scope, .ref, node); @@ -202,16 +201,16 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { /// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the /// result instruction can be used to inspect whether it is isNoReturn() but that is it, /// it must otherwise not be used. -pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { +pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { switch (node.tag) { - .Root => unreachable, // Top-level declaration. - .Use => unreachable, // Top-level declaration. - .TestDecl => unreachable, // Top-level declaration. - .DocComment => unreachable, // Top-level declaration. - .VarDecl => unreachable, // Handled in `blockExpr`. - .SwitchCase => unreachable, // Handled in `switchExpr`. - .SwitchElse => unreachable, // Handled in `switchExpr`. - .Range => unreachable, // Handled in `switchExpr`. + .root => unreachable, // Top-level declaration. + .@"usingnamespace" => unreachable, // Top-level declaration. + .test_decl => unreachable, // Top-level declaration. + .doc_comment => unreachable, // Top-level declaration. + .var_decl => unreachable, // Handled in `blockExpr`. + .switch_case => unreachable, // Handled in `switchExpr`. + .switch_else => unreachable, // Handled in `switchExpr`. + .range => unreachable, // Handled in `switchExpr`. .Else => unreachable, // Handled explicitly the control flow expression functions. .Payload => unreachable, // Handled explicitly. .PointerPayload => unreachable, // Handled explicitly. @@ -220,114 +219,113 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .FieldInitializer => unreachable, // Handled explicitly. .ContainerField => unreachable, // Handled explicitly. - .Assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)), - .AssignBitAnd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bit_and)), - .AssignBitOr => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bit_or)), - .AssignBitShiftLeft => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)), - .AssignBitShiftRight => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)), - .AssignBitXor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)), - .AssignDiv => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)), - .AssignSub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)), - .AssignSubWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)), - .AssignMod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)), - .AssignAdd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)), - .AssignAddWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)), - .AssignMul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)), - .AssignMulWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)), - - .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add), - .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap), - .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub), - .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap), - .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul), - .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap), - .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div), - .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem), - .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bit_and), - .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bit_or), - .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl), - .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr), - .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor), - - .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq), - .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq), - .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt), - .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte), - .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt), - .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte), - - .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat), - .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul), - - .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), - .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), - - .BoolNot => return rvalue(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), - .BitNot => return rvalue(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)), - .Negation => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)), - .NegationWrap => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)), - - .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), - .Asm => return rvalue(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), - .StringLiteral => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), - .IntegerLiteral => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)), - .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?), - .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?), - .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?), - .Return => return ret(mod, scope, node.castTag(.Return).?), - .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), - .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), - .Period => return field(mod, scope, rl, node.castTag(.Period).?), - .Deref => return rvalue(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), - .AddressOf => return rvalue(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), - .FloatLiteral => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), - .UndefinedLiteral => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), - .BoolLiteral => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), - .NullLiteral => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), - .OptionalType => return rvalue(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), - .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), - .Block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), - .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block), - .Break => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), - .Continue => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)), - .PtrType => return rvalue(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), - .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), - .ArrayType => return rvalue(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), - .ArrayTypeSentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), - .EnumLiteral => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)), - .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), - .CharLiteral => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), - .SliceType => return rvalue(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), - .ErrorUnion => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), - .MergeErrorSets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), - .AnyFrameType => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), - .ErrorSetDecl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)), - .ErrorType => return rvalue(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), - .For => return forExpr(mod, scope, rl, node.castTag(.For).?), - .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?), - .Slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)), - .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), - .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), - .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), - .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?), - .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?), - - .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), - .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), - .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), - .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), + .assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node)), + .assign_bit_and => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_and)), + .assign_bit_or => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_or)), + .assign_bit_shift_left => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shl)), + .assign_bit_shift_right => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shr)), + .assign_bit_xor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .xor)), + .assign_div => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .div)), + .assign_sub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .sub)), + .assign_sub_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .subwrap)), + .assign_mod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mod_rem)), + .assign_add => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .add)), + .assign_add_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .addwrap)), + .assign_mul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mul)), + .assign_mul_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mulwrap)), + + .add => return simpleBinOp(mod, scope, rl, node, .add), + .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap), + .sub => return simpleBinOp(mod, scope, rl, node, .sub), + .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap), + .mul => return simpleBinOp(mod, scope, rl, node, .mul), + .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap), + .div => return simpleBinOp(mod, scope, rl, node, .div), + .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem), + .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and), + .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or), + .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl), + .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr), + .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor), + + .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq), + .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq), + .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt), + .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte), + .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt), + .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte), + + .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat), + .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul), + + .bool_and => return boolBinOp(mod, scope, rl, node), + .bool_or => return boolBinOp(mod, scope, rl, node), + + .bool_not => return rvalue(mod, scope, rl, try boolNot(mod, scope, node)), + .bit_not => return rvalue(mod, scope, rl, try bitNot(mod, scope, node)), + .negation => return rvalue(mod, scope, rl, try negation(mod, scope, node, .sub)), + .negation_wrap => return rvalue(mod, scope, rl, try negation(mod, scope, node, .subwrap)), + + .identifier => return try identifier(mod, scope, rl, node), + .@"asm" => return rvalue(mod, scope, rl, try assembly(mod, scope, node)), + .string_literal => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node)), + .integer_literal => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node)), + .builtin_call => return builtinCall(mod, scope, rl, node), + .call => return callExpr(mod, scope, rl, node), + .@"unreachable" => return unreach(mod, scope, node), + .@"return" => return ret(mod, scope, node), + .@"if" => return ifExpr(mod, scope, rl, node), + .@"while" => return whileExpr(mod, scope, rl, node), + .period => return field(mod, scope, rl, node), + .deref => return rvalue(mod, scope, rl, try deref(mod, scope, node)), + .address_of => return rvalue(mod, scope, rl, try addressOf(mod, scope, node)), + .float_literal => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node)), + .undefined_literal => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node)), + .bool_literal => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node)), + .null_literal => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node)), + .optional_type => return rvalue(mod, scope, rl, try optionalType(mod, scope, node)), + .unwrap_optional => return unwrapOptional(mod, scope, rl, node), + .block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node)), + .labeled_block => return labeledBlockExpr(mod, scope, rl, node, .block), + .@"break" => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node)), + .@"continue" => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node)), + .grouped_expression => return expr(mod, scope, rl, node.expr), + .array_type => return rvalue(mod, scope, rl, try arrayType(mod, scope, node)), + .array_type_sentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node)), + .enum_literal => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node)), + .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node)), + .char_literal => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node)), + .slice_type => return rvalue(mod, scope, rl, try sliceType(mod, scope, node)), + .error_union => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node, .error_union_type)), + .merge_error_sets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node, .merge_error_sets)), + .anyframe_type => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node)), + .error_set_decl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node)), + .error_type => return rvalue(mod, scope, rl, try errorType(mod, scope, node)), + .@"for" => return forExpr(mod, scope, rl, node), + .array_access => return arrayAccess(mod, scope, rl, node), + .slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node)), + .@"catch" => return catchExpr(mod, scope, rl, node), + .@"comptime" => return comptimeKeyword(mod, scope, rl, node), + .@"orelse" => return orelseExpr(mod, scope, rl, node), + .@"switch" => return switchExpr(mod, scope, rl, node), + .ContainerDecl => return containerDecl(mod, scope, rl, node), + + .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), + .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), + .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), + .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}), .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), - .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), - .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), + .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), + .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), - .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}), + .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), } } -fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst { +fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.@"comptime") InnerError!*zir.Inst { const tracy = trace(@src()); defer tracy.end(); @@ -338,7 +336,7 @@ pub fn comptimeExpr( mod: *Module, parent_scope: *Scope, rl: ResultLoc, - node: *ast.Node, + node: ast.Node.Index, ) InnerError!*zir.Inst { // If we are already in a comptime scope, no need to make another one. if (parent_scope.isComptime()) { @@ -347,7 +345,7 @@ pub fn comptimeExpr( // Optimization for labeled blocks: don't need to have 2 layers of blocks, // we can reuse the existing one. - if (node.castTag(.LabeledBlock)) |block_node| { + if (node.castTag(.labeled_block)) |block_node| { return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime); } @@ -366,6 +364,8 @@ pub fn comptimeExpr( _ = try expr(mod, &block_scope.base, rl, node); const tree = parent_scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.firstToken()].start; const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{ @@ -381,6 +381,8 @@ fn breakExpr( node: *ast.Node.ControlFlowExpression, ) InnerError!*zir.Inst { const tree = parent_scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.ltoken].start; // Look for the label in the scope. @@ -445,6 +447,8 @@ fn breakExpr( fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { const tree = parent_scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.ltoken].start; // Look for the label in the scope. @@ -485,7 +489,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE } } -pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { +pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.block) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -502,6 +506,8 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn if (gen_zir.label) |prev_label| { if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { const tree = parent_scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const label_src = tree.token_locs[label].start; const prev_label_src = tree.token_locs[prev_label.token].start; @@ -539,7 +545,7 @@ fn labeledBlockExpr( mod: *Module, parent_scope: *Scope, rl: ResultLoc, - block_node: *ast.Node.LabeledBlock, + block_node: *ast.Node.labeled_block, zir_tag: zir.Inst.Tag, ) InnerError!*zir.Inst { const tracy = trace(@src()); @@ -548,6 +554,8 @@ fn labeledBlockExpr( assert(zir_tag == .block or zir_tag == .block_comptime); const tree = parent_scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[block_node.lbrace].start; try checkLabelRedefinition(mod, parent_scope, block_node.label); @@ -627,10 +635,12 @@ fn labeledBlockExpr( fn blockExprStmts( mod: *Module, parent_scope: *Scope, - node: *ast.Node, - statements: []*ast.Node, + node: ast.Node.Index, + statements: []const ast.Node.Index, ) !void { const tree = parent_scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); var block_arena = std.heap.ArenaAllocator.init(mod.gpa); defer block_arena.deinit(); @@ -640,24 +650,24 @@ fn blockExprStmts( const src = tree.token_locs[statement.firstToken()].start; _ = try addZIRNoOp(mod, scope, src, .dbg_stmt); switch (statement.tag) { - .VarDecl => { - const var_decl_node = statement.castTag(.VarDecl).?; + .var_decl => { + const var_decl_node = statement.castTag(.var_decl).?; scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator); }, - .Assign => try assign(mod, scope, statement.castTag(.Assign).?), - .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bit_and), - .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bit_or), - .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl), - .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr), - .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor), - .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div), - .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub), - .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap), - .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem), - .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add), - .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap), - .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul), - .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap), + .assign => try assign(mod, scope, statement), + .assign_bit_and => try assignOp(mod, scope, statement, .bit_and), + .assign_bit_or => try assignOp(mod, scope, statement, .bit_or), + .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl), + .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr), + .assign_bit_xor => try assignOp(mod, scope, statement, .xor), + .assign_div => try assignOp(mod, scope, statement, .div), + .assign_sub => try assignOp(mod, scope, statement, .sub), + .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap), + .assign_mod => try assignOp(mod, scope, statement, .mod_rem), + .assign_add => try assignOp(mod, scope, statement, .add), + .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap), + .assign_mul => try assignOp(mod, scope, statement, .mul), + .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap), else => { const possibly_unused_result = try expr(mod, scope, .none, statement); @@ -672,7 +682,7 @@ fn blockExprStmts( fn varDecl( mod: *Module, scope: *Scope, - node: *ast.Node.VarDecl, + node: *ast.Node.var_decl, block_arena: *Allocator, ) InnerError!*Scope { if (node.getComptimeToken()) |comptime_token| { @@ -682,6 +692,8 @@ fn varDecl( return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{}); } const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const name_src = tree.token_locs[node.name_token].start; const ident_name = try mod.identifierTokenString(scope, node.name_token); @@ -733,7 +745,7 @@ fn varDecl( return mod.fail(scope, name_src, "variables must be initialized", .{}); switch (tree.token_ids[node.mut_token]) { - .Keyword_const => { + .keyword_const => { // Depending on the type of AST the initialization expression is, we may need an lvalue // or an rvalue as a result location. If it is an rvalue, we can use the instruction as // the variable, no memory location needed. @@ -834,7 +846,7 @@ fn varDecl( }; return &sub_scope.base; }, - .Keyword_var => { + .keyword_var => { var resolve_inferred_alloc: ?*zir.Inst = null; const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: { const type_inst = try typeExpr(mod, scope, type_node); @@ -862,33 +874,39 @@ fn varDecl( } } -fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void { - if (infix_node.lhs.castTag(.Identifier)) |ident| { - // This intentionally does not support @"_" syntax. - const ident_name = scope.tree().tokenSlice(ident.token); +fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void { + const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); + const lhs = node_datas[infix_node].lhs; + const rhs = node_datas[infix_node].rhs; + if (node_tags[lhs] == .identifier) { + // This intentionally does not support `@"_"` syntax. + const ident_name = tree.tokenSlice(main_tokens[lhs]); if (mem.eql(u8, ident_name, "_")) { _ = try expr(mod, scope, .discard, infix_node.rhs); return; } } - const lvalue = try lvalExpr(mod, scope, infix_node.lhs); - _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs); + const lvalue = try lvalExpr(mod, scope, lhs); + _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs); } fn assignOp( mod: *Module, scope: *Scope, - infix_node: *ast.Node.SimpleInfixOp, + infix_node: ast.Node.Index, op_inst_tag: zir.Inst.Tag, ) InnerError!void { - const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs); - const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); - const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); - const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs); - const tree = scope.tree(); - const src = tree.token_locs[infix_node.op_token].start; + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); + const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs); + const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); + const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); + const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); + const src = token_starts[main_tokens[infix_node]]; const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result); } @@ -935,7 +953,7 @@ fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) Inn return addZIRUnOp(mod, scope, src, .optional_type, operand); } -fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst { +fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.slice_type) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice); @@ -948,7 +966,7 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir .Asterisk, .AsteriskAsterisk => .One, // TODO stage1 type inference bug .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) { - .Identifier => .C, + .identifier => .C, else => .Many, }), else => unreachable, @@ -998,7 +1016,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); } -fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { +fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.array_type) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; const usize_type = try addZIRInstConst(mod, scope, src, .{ @@ -1013,7 +1031,7 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst return addZIRBinOp(mod, scope, src, .array_type, len, elem_type); } -fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { +fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.array_type_sentinel) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; const usize_type = try addZIRInstConst(mod, scope, src, .{ @@ -1034,7 +1052,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti }, .{}); } -fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst { +fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.anyframe_type) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.anyframe_token].start; if (node.result) |some| { @@ -1056,7 +1074,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload); } -fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { +fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.enum_literal) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.name].start; const name = try mod.identifierTokenString(scope, node.name); @@ -1141,13 +1159,13 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con var layout: std.builtin.TypeInfo.ContainerLayout = .Auto; if (node.layout_token) |some| switch (tree.token_ids[some]) { - .Keyword_extern => layout = .Extern, - .Keyword_packed => layout = .Packed, + .keyword_extern => layout = .Extern, + .keyword_packed => layout = .Packed, else => unreachable, }; const container_type = switch (tree.token_ids[node.kind_token]) { - .Keyword_enum => blk: { + .keyword_enum => blk: { const tag_type: ?*zir.Inst = switch (node.init_arg_expr) { .Type => |t| try typeExpr(mod, &gen_scope.base, t), .None => null, @@ -1174,7 +1192,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con }; break :blk Type.initPayload(&enum_type.base); }, - .Keyword_struct => blk: { + .keyword_struct => blk: { assert(node.init_arg_expr == .None); const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{ .fields = try arena.dupe(*zir.Inst, fields.items), @@ -1196,7 +1214,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con }; break :blk Type.initPayload(&struct_type.base); }, - .Keyword_union => blk: { + .keyword_union => blk: { const init_inst = switch (node.init_arg_expr) { .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null, .None => null, @@ -1229,7 +1247,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con }; break :blk Type.initPayload(&union_type.base); }, - .Keyword_opaque => blk: { + .keyword_opaque => blk: { if (fields.items.len > 0) { return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{}); } @@ -1258,7 +1276,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con } } -fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { +fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.error_set_decl) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.error_token].start; const decls = node.decls(); @@ -1281,7 +1299,7 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!* }); } -fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst { +fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.@"catch") InnerError!*zir.Inst { switch (rl) { .ref => return orelseCatchExpr( mod, @@ -1528,7 +1546,7 @@ pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleI const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; // TODO custom AST node for field access so that we don't have to go through a node cast here - const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.Identifier).?.token); + const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.identifier).?.token); if (rl == .ref) { return addZirInstTag(mod, scope, src, .field_ptr, .{ .object = try expr(mod, scope, .ref, node.lhs), @@ -1545,7 +1563,7 @@ fn namedField( mod: *Module, scope: *Scope, rl: ResultLoc, - call: *ast.Node.BuiltinCall, + call: *ast.Node.builtin_call, ) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 2); @@ -1571,7 +1589,7 @@ fn namedField( })); } -fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst { +fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.array_access) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.rtoken].start; const usize_type = try addZIRInstConst(mod, scope, src, .{ @@ -1592,7 +1610,7 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array })); } -fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst { +fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.slice) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.rtoken].start; @@ -1633,15 +1651,16 @@ fn simpleBinOp( mod: *Module, scope: *Scope, rl: ResultLoc, - infix_node: *ast.Node.SimpleInfixOp, + infix_node: ast.Node.Index, op_inst_tag: zir.Inst.Tag, ) InnerError!*zir.Inst { const tree = scope.tree(); - const src = tree.token_locs[infix_node.op_token].start; - - const lhs = try expr(mod, scope, .none, infix_node.lhs); - const rhs = try expr(mod, scope, .none, infix_node.rhs); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); + const lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs); + const rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs); + const src = token_starts[main_tokens[infix_node]]; const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); return rvalue(mod, scope, rl, result); } @@ -1653,6 +1672,9 @@ fn boolBinOp( infix_node: *ast.Node.SimpleInfixOp, ) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); + const src = tree.token_locs[infix_node.op_token].start; const bool_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), @@ -1703,7 +1725,7 @@ fn boolBinOp( }; defer const_scope.instructions.deinit(mod.gpa); - const is_bool_and = infix_node.base.tag == .BoolAnd; + const is_bool_and = infix_node.base.tag == .bool_and; _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{ .block = block, .operand = try addZIRInstConst(mod, &const_scope.base, src, .{ @@ -1769,7 +1791,7 @@ const CondKind = union(enum) { return &then_scope.base; }; const is_ptr = payload.ptr_token != null; - const ident_node = payload.value_symbol.castTag(.Identifier).?; + const ident_node = payload.value_symbol.castTag(.identifier).?; // This intentionally does not support @"_" syntax. const ident_name = then_scope.base.tree().tokenSlice(ident_node.token); @@ -1788,7 +1810,7 @@ const CondKind = union(enum) { const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .err_union_payload_unsafe_ptr, self.err_union.?); const payload = payload_node.?.castTag(.Payload).?; - const ident_node = payload.error_symbol.castTag(.Identifier).?; + const ident_node = payload.error_symbol.castTag(.identifier).?; // This intentionally does not support @"_" syntax. const ident_name = else_scope.base.tree().tokenSlice(ident_node.token); @@ -1800,7 +1822,7 @@ const CondKind = union(enum) { } }; -fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst { +fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if") InnerError!*zir.Inst { var cond_kind: CondKind = .bool; if (if_node.payload) |_| cond_kind = .{ .optional = null }; if (if_node.@"else") |else_node| { @@ -1819,6 +1841,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn defer block_scope.instructions.deinit(mod.gpa); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const if_src = tree.token_locs[if_node.if_token].start; const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition); @@ -1918,7 +1942,7 @@ fn whileExpr( mod: *Module, scope: *Scope, rl: ResultLoc, - while_node: *ast.Node.While, + while_node: *ast.Node.@"while", ) InnerError!*zir.Inst { var cond_kind: CondKind = .bool; if (while_node.payload) |_| cond_kind = .{ .optional = null }; @@ -1955,6 +1979,8 @@ fn whileExpr( defer continue_scope.instructions.deinit(mod.gpa); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const while_src = tree.token_locs[while_node.while_token].start; const void_type = try addZIRInstConst(mod, scope, while_src, .{ .ty = Type.initTag(.type), @@ -2066,7 +2092,7 @@ fn forExpr( mod: *Module, scope: *Scope, rl: ResultLoc, - for_node: *ast.Node.For, + for_node: *ast.Node.@"for", ) InnerError!*zir.Inst { if (for_node.label) |label| { try checkLabelRedefinition(mod, scope, label); @@ -2077,6 +2103,8 @@ fn forExpr( // setup variables and constants const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const for_src = tree.token_locs[for_node.for_token].start; const index_ptr = blk: { const usize_type = try addZIRInstConst(mod, scope, for_src, .{ @@ -2246,9 +2274,9 @@ fn forExpr( ); } -fn switchCaseUsesRef(node: *ast.Node.Switch) bool { +fn switchCaseUsesRef(node: *ast.Node.@"switch") bool { for (node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.SwitchCase).?; + const case = uncasted_case.castTag(.switch_case).?; const uncasted_payload = case.payload orelse continue; const payload = uncasted_payload.castTag(.PointerPayload).?; if (payload.ptr_token) |_| return true; @@ -2260,15 +2288,17 @@ fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { var cur = node; while (true) { switch (cur.tag) { - .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), - .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr, + .range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), + .grouped_expression => cur = @fieldParentPtr(ast.Node.grouped_expression, "base", cur).expr, else => return null, } } } -fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { +fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.@"switch") InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const switch_src = tree.token_locs[switch_node.switch_token].start; const use_ref = switchCaseUsesRef(switch_node); @@ -2291,12 +2321,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node var first_range: ?*zir.Inst = null; var simple_case_count: usize = 0; for (switch_node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.SwitchCase).?; + const case = uncasted_case.castTag(.switch_case).?; const case_src = tree.token_locs[case.firstToken()].start; assert(case.items_len != 0); // Check for else/_ prong, those are handled last. - if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { + if (case.items_len == 1 and case.items()[0].tag == .switch_else) { if (else_src) |src| { const msg = msg: { const msg = try mod.errMsg( @@ -2313,7 +2343,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node } else_src = case_src; continue; - } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and + } else if (case.items_len == 1 and case.items()[0].tag == .identifier and mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) { if (underscore_src) |src| { @@ -2412,20 +2442,20 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node defer else_scope.instructions.deinit(mod.gpa); // Now generate all but the special cases - var special_case: ?*ast.Node.SwitchCase = null; + var special_case: ?*ast.Node.switch_case = null; var items_index: usize = 0; var case_index: usize = 0; for (switch_node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.SwitchCase).?; + const case = uncasted_case.castTag(.switch_case).?; const case_src = tree.token_locs[case.firstToken()].start; // reset without freeing to reduce allocations. case_scope.instructions.items.len = 0; // Check for else/_ prong, those are handled last. - if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { + if (case.items_len == 1 and case.items()[0].tag == .switch_else) { special_case = case; continue; - } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and + } else if (case.items_len == 1 and case.items()[0].tag == .identifier and mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) { special_case = case; @@ -2528,11 +2558,13 @@ fn switchCaseExpr( scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, - case: *ast.Node.SwitchCase, + case: *ast.Node.switch_case, target: *zir.Inst, target_ptr: ?*zir.Inst, ) !void { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const case_src = tree.token_locs[case.firstToken()].start; const sub_scope = blk: { const uncasted_payload = case.payload orelse break :blk scope; @@ -2559,6 +2591,8 @@ fn switchCaseExpr( fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[cfe.ltoken].start; if (cfe.getRHS()) |rhs_node| { if (nodeMayNeedMemoryLocation(rhs_node, scope)) { @@ -2580,6 +2614,8 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo defer tracy.end(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const ident_name = try mod.identifierTokenString(scope, ident.token); const src = tree.token_locs[ident.token].start; if (mem.eql(u8, ident_name, "_")) { @@ -2667,6 +2703,8 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const unparsed_bytes = tree.tokenSlice(str_lit.token); const arena = scope.arena(); @@ -2686,6 +2724,8 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) Inner fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const lines = node.linesConst(); const src = tree.token_locs[lines[0]].start; @@ -2713,6 +2753,8 @@ fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStr fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.token].start; const slice = tree.tokenSlice(node.token); @@ -2733,6 +2775,8 @@ fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const prefixed_bytes = tree.tokenSlice(int_lit.token); const base = if (mem.startsWith(u8, prefixed_bytes, "0x")) 16 @@ -2762,6 +2806,8 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const bytes = tree.tokenSlice(float_lit.token); if (bytes.len > 2 and bytes[1] == 'x') { return mod.failTok(scope, float_lit.token, "TODO hex floats", .{}); @@ -2780,6 +2826,8 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.token].start; return addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.@"undefined"), @@ -2790,12 +2838,14 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.token].start; return addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.bool), .val = switch (tree.token_ids[node.token]) { - .Keyword_true => Value.initTag(.bool_true), - .Keyword_false => Value.initTag(.bool_false), + .keyword_true => Value.initTag(.bool_true), + .keyword_false => Value.initTag(.bool_false), else => unreachable, }, }); @@ -2804,6 +2854,8 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[node.token].start; return addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.@"null"), @@ -2811,12 +2863,14 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError }); } -fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { +fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.@"asm") InnerError!*zir.Inst { if (asm_node.outputs.len != 0) { return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); } const arena = scope.arena(); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len); const args = try arena.alloc(*zir.Inst, asm_node.inputs.len); @@ -2839,7 +2893,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi .ty = Type.initTag(.type), .val = Value.initTag(.void_type), }); - const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{ + const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.@"asm", .{ .asm_source = try expr(mod, scope, str_type_rl, asm_node.template), .return_type = return_type, }, .{ @@ -2851,7 +2905,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi return asm_inst; } -fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void { +fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call, count: u32) !void { if (call.params_len == count) return; @@ -2863,11 +2917,13 @@ fn simpleCast( mod: *Module, scope: *Scope, rl: ResultLoc, - call: *ast.Node.BuiltinCall, + call: *ast.Node.builtin_call, inst_tag: zir.Inst.Tag, ) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 2); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const dest_type = try typeExpr(mod, scope, params[0]); @@ -2876,10 +2932,12 @@ fn simpleCast( return rvalue(mod, scope, rl, result); } -fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 1); const operand = try expr(mod, scope, .none, call.params()[0]); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; return addZIRUnOp(mod, scope, src, .ptrtoint, operand); } @@ -2888,10 +2946,12 @@ fn as( mod: *Module, scope: *Scope, rl: ResultLoc, - call: *ast.Node.BuiltinCall, + call: *ast.Node.builtin_call, ) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 2); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const dest_type = try typeExpr(mod, scope, params[0]); @@ -2963,9 +3023,11 @@ fn asRlPtr( } } -fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.builtin_call) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 2); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const dest_type = try typeExpr(mod, scope, params[0]); @@ -3007,27 +3069,33 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa } } -fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn import(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 1); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const target = try expr(mod, scope, .none, params[0]); return addZIRUnOp(mod, scope, src, .import, target); } -fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 1); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const target = try expr(mod, scope, .none, params[0]); return addZIRUnOp(mod, scope, src, .compile_error, target); } -fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst { try ensureBuiltinParamCount(mod, scope, call, 1); const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const u32_type = try addZIRInstConst(mod, scope, src, .{ @@ -3038,8 +3106,10 @@ fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota); } -fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.builtin_call) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const arena = scope.arena(); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); @@ -3054,8 +3124,10 @@ fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCal items[param_i] = try expr(mod, scope, .none, param); return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{})); } -fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const arena = scope.arena(); const src = tree.token_locs[call.builtin_token].start; const params = call.params(); @@ -3065,8 +3137,10 @@ fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerErr return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{}); } -fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { +fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.builtin_call) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const builtin_name = tree.tokenSlice(call.builtin_token); // We handle the different builtins manually because they have different semantics depending @@ -3104,8 +3178,10 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built } } -fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst { +fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.call) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const lhs = try expr(mod, scope, .none, node.lhs); const param_nodes = node.params(); @@ -3130,6 +3206,8 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst { const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); const src = tree.token_locs[unreach_node.token].start; return addZIRNoOp(mod, scope, src, .unreachable_safe); } @@ -3176,11 +3254,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { while (true) { switch (node.tag) { .Root, - .Use, - .TestDecl, - .DocComment, - .SwitchCase, - .SwitchElse, + .@"usingnamespace", + .test_decl, + .doc_comment, + .switch_case, + .switch_else, .Else, .Payload, .PointerPayload, @@ -3190,97 +3268,97 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .FieldInitializer, => unreachable, - .Return, - .Break, - .Continue, - .BitNot, - .BoolNot, - .VarDecl, - .Defer, - .AddressOf, - .OptionalType, - .Negation, - .NegationWrap, - .Resume, - .ArrayType, - .ArrayTypeSentinel, + .@"return", + .@"break", + .@"continue", + .bit_not, + .bool_not, + .var_decl, + .@"defer", + .address_of, + .optional_type, + .negation, + .negation_wrap, + .@"resume", + .array_type, + .array_type_sentinel, .PtrType, - .SliceType, - .Suspend, - .AnyType, - .ErrorType, + .slice_type, + .@"suspend", + .@"anytype", + .error_type, .FnProto, - .AnyFrameType, - .IntegerLiteral, - .FloatLiteral, - .EnumLiteral, - .StringLiteral, + .anyframe_type, + .integer_literal, + .float_literal, + .enum_literal, + .string_literal, .MultilineStringLiteral, - .CharLiteral, - .BoolLiteral, - .NullLiteral, - .UndefinedLiteral, - .Unreachable, - .Identifier, - .ErrorSetDecl, + .char_literal, + .bool_literal, + .null_literal, + .undefined_literal, + .@"unreachable", + .identifier, + .error_set_decl, .ContainerDecl, - .Asm, - .Add, - .AddWrap, - .ArrayCat, - .ArrayMult, - .Assign, - .AssignBitAnd, - .AssignBitOr, - .AssignBitShiftLeft, - .AssignBitShiftRight, - .AssignBitXor, - .AssignDiv, - .AssignSub, - .AssignSubWrap, - .AssignMod, - .AssignAdd, - .AssignAddWrap, - .AssignMul, - .AssignMulWrap, - .BangEqual, - .BitAnd, - .BitOr, - .BitShiftLeft, - .BitShiftRight, - .BitXor, - .BoolAnd, - .BoolOr, - .Div, - .EqualEqual, - .ErrorUnion, - .GreaterOrEqual, - .GreaterThan, - .LessOrEqual, - .LessThan, - .MergeErrorSets, - .Mod, - .Mul, - .MulWrap, - .Range, - .Period, - .Sub, - .SubWrap, - .Slice, - .Deref, - .ArrayAccess, - .Block, + .@"asm", + .add, + .add_wrap, + .array_cat, + .array_mult, + .assign, + .assign_bit_and, + .assign_bit_or, + .assign_bit_shift_left, + .assign_bit_shift_right, + .assign_bit_xor, + .assign_div, + .assign_sub, + .assign_sub_wrap, + .assign_mod, + .assign_add, + .assign_add_wrap, + .assign_mul, + .assign_mul_wrap, + .bang_equal, + .bit_and, + .bit_or, + .bit_shift_left, + .bit_shift_right, + .bit_xor, + .bool_and, + .bool_or, + .div, + .equal_equal, + .error_union, + .greater_or_equal, + .greater_than, + .less_or_equal, + .less_than, + .merge_error_sets, + .mod, + .mul, + .mul_wrap, + .range, + .period, + .sub, + .sub_wrap, + .slice, + .deref, + .array_access, + .block, => return false, // Forward the question to a sub-expression. - .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr, - .Try => node = node.castTag(.Try).?.rhs, - .Await => node = node.castTag(.Await).?.rhs, - .Catch => node = node.castTag(.Catch).?.rhs, - .OrElse => node = node.castTag(.OrElse).?.rhs, - .Comptime => node = node.castTag(.Comptime).?.expr, - .Nosuspend => node = node.castTag(.Nosuspend).?.expr, - .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs, + .grouped_expression => node = node.castTag(.grouped_expression).?.expr, + .@"try" => node = node.castTag(.@"try").?.rhs, + .@"await" => node = node.castTag(.@"await").?.rhs, + .@"catch" => node = node.castTag(.@"catch").?.rhs, + .@"orelse" => node = node.castTag(.@"orelse").?.rhs, + .@"comptime" => node = node.castTag(.@"comptime").?.expr, + .@"nosuspend" => node = node.castTag(.@"nosuspend").?.expr, + .unwrap_optional => node = node.castTag(.unwrap_optional).?.lhs, // True because these are exactly the expressions we need memory locations for. .ArrayInitializer, @@ -3291,14 +3369,14 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { // True because depending on comptime conditions, sub-expressions // may be the kind that need memory locations. - .While, - .For, - .Switch, - .Call, - .LabeledBlock, + .@"while", + .@"for", + .@"switch", + .call, + .labeled_block, => return true, - .BuiltinCall => { + .builtin_call => { @setEvalBranchQuota(5000); const builtin_needs_mem_loc = std.ComptimeStringMap(bool, .{ .{ "@addWithOverflow", false }, @@ -3404,12 +3482,12 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .{ "@TypeOf", false }, .{ "@unionInit", true }, }); - const name = scope.tree().tokenSlice(node.castTag(.BuiltinCall).?.builtin_token); + const name = scope.tree().tokenSlice(node.castTag(.builtin_call).?.builtin_token); return builtin_needs_mem_loc.get(name).?; }, // Depending on AST properties, they may need memory locations. - .If => return node.castTag(.If).?.@"else" != null, + .@"if" => return node.castTag(.@"if").?.@"else" != null, } } } @@ -3450,8 +3528,17 @@ fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr } } -fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst { - const src = scope.tree().token_locs[node.firstToken()].start; +fn rvalueVoid( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, + result: void, +) InnerError!*zir.Inst { + const tree = scope.tree(); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); + const src = tree.tokens.items(.start)[tree.firstToken(node)]; const void_inst = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value), diff --git a/src/codegen.zig b/src/codegen.zig index 9771386403..095bb123ba 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -451,11 +451,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: { const container_scope = module_fn.owner_decl.container; - const tree = container_scope.file_scope.contents.tree; - const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const lbrace_src = tree.token_locs[block.lbrace].start; - const rbrace_src = tree.token_locs[block.rbrace].start; + const tree = container_scope.file_scope.tree; + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const token_starts = tree.tokens.items(.start); + + const fn_decl = tree.rootDecls()[module_fn.owner_decl.src_index]; + assert(node_tags[fn_decl] == .fn_decl); + const block = node_datas[fn_decl].rhs; + const lbrace_src = token_starts[tree.firstToken(block)]; + const rbrace_src = token_starts[tree.lastToken(block)]; break :blk .{ .lbrace_src = lbrace_src, .rbrace_src = rbrace_src, diff --git a/src/ir.zig b/src/ir.zig index 0e83dbfd56..a0b33fba73 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -317,6 +317,7 @@ pub const Inst = struct { pub const base_tag = Tag.arg; base: Inst, + /// This exists to be emitted into debug info. name: [*:0]const u8, pub fn operandCount(self: *const Arg) usize { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 18f3f57712..f92c585cd5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2223,13 +2223,19 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { try dbg_line_buffer.ensureCapacity(26); const line_off: u28 = blk: { - const tree = decl.container.file_scope.contents.tree; - const file_ast_decls = tree.root_node.decls(); + const tree = decl.container.file_scope.tree; + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const token_starts = tree.tokens.items(.start); + + const file_ast_decls = tree.rootDecls(); // TODO Look into improving the performance here by adding a token-index-to-line // lookup table. Currently this involves scanning over the source code for newlines. - const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); + const fn_decl = file_ast_decls[decl.src_index]; + assert(node_tags[fn_decl] == .fn_decl); + const block = node_datas[fn_decl].rhs; + const lbrace = tree.firstToken(block); + const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); break :blk @intCast(u28, line_delta); }; @@ -2744,13 +2750,19 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec if (self.llvm_ir_module) |_| return; - const tree = decl.container.file_scope.contents.tree; - const file_ast_decls = tree.root_node.decls(); + const tree = decl.container.file_scope.tree; + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const token_starts = tree.tokens.items(.start); + + const file_ast_decls = tree.rootDecls(); // TODO Look into improving the performance here by adding a token-index-to-line // lookup table. Currently this involves scanning over the source code for newlines. - const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); + const fn_decl = file_ast_decls[decl.src_index]; + assert(node_tags[fn_decl] == .fn_decl); + const block = node_datas[fn_decl].rhs; + const lbrace = tree.firstToken(block); + const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); const casted_line_off = @intCast(u28, line_delta); const shdr = &self.sections.items[self.debug_line_section_index.?]; diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 15aa86be51..645e17068b 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -904,13 +904,19 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M const tracy = trace(@src()); defer tracy.end(); - const tree = decl.container.file_scope.contents.tree; - const file_ast_decls = tree.root_node.decls(); + const tree = decl.container.file_scope.tree; + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const token_starts = tree.tokens.items(.start); + + const file_ast_decls = tree.rootDecls(); // TODO Look into improving the performance here by adding a token-index-to-line // lookup table. Currently this involves scanning over the source code for newlines. - const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); + const fn_decl = file_ast_decls[decl.src_index]; + assert(node_tags[fn_decl] == .fn_decl); + const block = node_datas[fn_decl].rhs; + const lbrace = tree.firstToken(block); + const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); const casted_line_off = @intCast(u28, line_delta); const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; @@ -948,13 +954,19 @@ pub fn initDeclDebugBuffers( try dbg_line_buffer.ensureCapacity(26); const line_off: u28 = blk: { - const tree = decl.container.file_scope.contents.tree; - const file_ast_decls = tree.root_node.decls(); + const tree = decl.container.file_scope.tree; + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const token_starts = tree.tokens.items(.start); + + const file_ast_decls = tree.rootDecls(); // TODO Look into improving the performance here by adding a token-index-to-line // lookup table. Currently this involves scanning over the source code for newlines. - const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); + const fn_decl = file_ast_decls[decl.src_index]; + assert(node_tags[fn_decl] == .fn_decl); + const block = node_datas[fn_decl].rhs; + const lbrace = tree.firstToken(block); + const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); break :blk @intCast(u28, line_delta); }; diff --git a/src/zir.zig b/src/zir.zig index d8ac023562..fc68aee216 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -53,6 +53,9 @@ pub const Inst = struct { indexable_ptr_len, /// Function parameter value. These must be first in a function's main block, /// in respective order with the parameters. + /// TODO make this instruction implicit; after we transition to having ZIR + /// instructions be same sized and referenced by index, the first N indexes + /// will implicitly be references to the parameters of the function. arg, /// Type coercion. as, @@ -354,9 +357,8 @@ pub const Inst = struct { .return_void, .ret_ptr, .ret_type, - .unreach_nocheck, - .@"unreachable", - .arg, + .unreachable_unsafe, + .unreachable_safe, .void_value, => NoOp, @@ -451,6 +453,7 @@ pub const Inst = struct { .block_comptime_flat, => Block, + .arg => Arg, .array_type_sentinel => ArrayTypeSentinel, .@"break" => Break, .break_void => BreakVoid, @@ -684,6 +687,18 @@ pub const Inst = struct { kw_args: struct {}, }; + pub const Arg = struct { + pub const base_tag = Tag.arg; + base: Inst, + + positionals: struct { + /// This exists to be passed to the arg TZIR instruction, which + /// needs it for debug info. + name: []const u8, + }, + kw_args: struct {}, + }; + pub const Block = struct { pub const base_tag = Tag.block; base: Inst, -- cgit v1.2.3 From c66481f9bcc5c08b92ccfd5d38d9d72be83479c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Feb 2021 20:59:21 -0700 Subject: astgen: finish updating expressions to new mem layout Now all that is left is compile errors and whatever regressions this branch introduced. --- lib/std/zig/ast.zig | 7 +- lib/std/zig/render.zig | 6 +- src/Module.zig | 16 +- src/astgen.zig | 721 +++++++++++++++++++++++++++++-------------------- src/zir.zig | 18 +- src/zir_sema.zig | 12 +- 6 files changed, 455 insertions(+), 325 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 18743c19d9..02bca79986 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1528,8 +1528,9 @@ pub const Tree = struct { pub fn switchCaseOne(tree: Tree, node: Node.Index) full.SwitchCase { const data = &tree.nodes.items(.data)[node]; + const values: *[1]Node.Index = &data.lhs; return tree.fullSwitchCase(.{ - .values = if (data.lhs == 0) &.{} else @ptrCast([*]Node.Index, &data.lhs)[0..1], + .values = if (data.lhs == 0) values[0..0] else values[0..1], .arrow_token = tree.nodes.items(.main_token)[node], .target_expr = data.rhs, }); @@ -2532,8 +2533,8 @@ pub const Node = struct { @"defer", /// lhs catch rhs /// lhs catch |err| rhs - /// main_token is the catch - /// payload is determined by looking at the prev tokens before rhs. + /// main_token is the `catch` keyword. + /// payload is determined by looking at the next token after the `catch` keyword. @"catch", /// `lhs.a`. main_token is the dot. rhs is the identifier token index. field_access, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 3b5765507b..f7608fe61a 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -416,9 +416,9 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac return renderToken(ais, tree, rbracket, space); // ] }, - .slice_open => try renderSlice(ais, tree, tree.sliceOpen(node), space), - .slice => try renderSlice(ais, tree, tree.slice(node), space), - .slice_sentinel => try renderSlice(ais, tree, tree.sliceSentinel(node), space), + .slice_open => return renderSlice(ais, tree, tree.sliceOpen(node), space), + .slice => return renderSlice(ais, tree, tree.slice(node), space), + .slice_sentinel => return renderSlice(ais, tree, tree.sliceSentinel(node), space), .deref => { try renderExpression(ais, tree, datas[node].lhs, .none); diff --git a/src/Module.zig b/src/Module.zig index 2071ff671c..19566dee43 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1163,16 +1163,16 @@ fn astgenAndSemaFn( } assert(param_type_i == param_count); } - if (fn_proto.lib_name) |lib_name| blk: { + if (fn_proto.lib_name) |lib_name_token| blk: { // TODO call std.zig.parseStringLiteral - const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name), "\""); + const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name_token), "\""); log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); const target = mod.comp.getTarget(); if (target_util.is_libc_lib_name(target, lib_name_str)) { if (!mod.comp.bin_file.options.link_libc) { return mod.failTok( &fn_type_scope.base, - lib_name, + lib_name_token, "dependency on libc must be explicitly specified in the build command", .{}, ); @@ -1183,7 +1183,7 @@ fn astgenAndSemaFn( if (!mod.comp.bin_file.options.link_libcpp) { return mod.failTok( &fn_type_scope.base, - lib_name, + lib_name_token, "dependency on libc++ must be explicitly specified in the build command", .{}, ); @@ -1193,17 +1193,17 @@ fn astgenAndSemaFn( if (!target.isWasm() and !mod.comp.bin_file.options.pic) { return mod.failTok( &fn_type_scope.base, - lib_name, + lib_name_token, "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", - .{ lib_name, lib_name }, + .{ lib_name_str, lib_name_str }, ); } mod.comp.stage1AddLinkLib(lib_name_str) catch |err| { return mod.failTok( &fn_type_scope.base, - lib_name, + lib_name_token, "unable to add link lib '{s}': {s}", - .{ lib_name, @errorName(err) }, + .{ lib_name_str, @errorName(err) }, ); }; } diff --git a/src/astgen.zig b/src/astgen.zig index 56d1497f63..aef1b21a6c 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -327,6 +327,15 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .while_cont => return whileExpr(mod, scope, tree.whileCont(node)), .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)), + .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)), + .@"for" => return forExpr(mod, scope, rl, tree.forFull(node)), + + // TODO handling these separately would actually be simpler & have fewer branches + // once we have a ZIR instruction for each of these 3 cases. + .slice_open => return sliceExpr(mod, scope, rl, tree.sliceOpen(node)), + .slice => return sliceExpr(mod, scope, rl, tree.slice(node)), + .slice_sentinel => return sliceExpr(mod, scope, rl, tree.sliceSentinel(node)), + .deref => { const lhs = try expr(mod, scope, .none, node_datas[node].lhs); const src = token_starts[main_tokens[node]]; @@ -402,51 +411,122 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; return blockExpr(mod, scope, rl, node, statements); }, - - .@"break" => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node)), - .@"continue" => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node)), - .grouped_expression => return expr(mod, scope, rl, node.expr), - .array_type => return rvalue(mod, scope, rl, try arrayType(mod, scope, node)), - .array_type_sentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node)), - .enum_literal => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node)), - .char_literal => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node)), - .slice_type => return rvalue(mod, scope, rl, try sliceType(mod, scope, node)), - .error_union => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node, .error_union_type)), - .merge_error_sets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node, .merge_error_sets)), - .anyframe_type => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node)), - .error_set_decl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node)), - .error_type => return rvalue(mod, scope, rl, try errorType(mod, scope, node)), - .@"for" => return forExpr(mod, scope, rl, node), + .enum_literal => { + const ident_token = main_tokens[node]; + const name = try mod.identifierTokenString(scope, ident_token); + const src = token_starts[ident_token]; + const result = try addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); + return rvalue(mod, scope, rl, result); + }, + .error_union => { + const error_set = try typeExpr(mod, scope, node_datas[node].lhs); + const payload = try typeExpr(mod, scope, node_datas[node].rhs); + const src = token_starts[main_tokens[node]]; + const result = try addZIRBinOp(mod, scope, src, .error_union_type, error_set, payload); + return rvalue(mod, scope, rl, result); + }, + .merge_error_sets => { + const lhs = try typeExpr(mod, scope, node_datas[node].lhs); + const rhs = try typeExpr(mod, scope, node_datas[node].rhs); + const src = token_starts[main_tokens[node]]; + const result = try addZIRBinOp(mod, scope, src, .merge_error_sets, lhs, rhs); + return rvalue(mod, scope, rl, result); + }, + .anyframe_literal => { + const main_token = main_tokens[node]; + const src = token_starts[main_token]; + const result = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.anyframe_type), + }); + return rvalue(mod, scope, rl, result); + }, + .anyframe_type => { + const src = token_starts[node_datas[node].lhs]; + const return_type = try typeExpr(mod, scope, node_datas[node].rhs); + const result = try addZIRUnOp(mod, scope, src, .anyframe_type, return_type); + return rvalue(mod, scope, rl, result); + }, + .@"catch" => { + const catch_token = main_tokens[node]; + const payload_token: ?TokenIndex = if (token_tags[catch_token + 1] == .pipe) + catch_token + 2 + else + null; + switch (rl) { + .ref => return orelseCatchExpr( + mod, + scope, + rl, + node_datas[node].lhs, + main_tokens[node], + .is_err_ptr, + .err_union_payload_unsafe_ptr, + .err_union_code_ptr, + node_datas[node].rhs, + payload_token, + ), + else => return orelseCatchExpr( + mod, + scope, + rl, + node_datas[node].lhs, + main_tokens[node], + .is_err, + .err_union_payload_unsafe, + .err_union_code, + node_datas[node].rhs, + payload_token, + ), + } + }, + .@"orelse" => switch (rl) { + .ref => return orelseCatchExpr( + mod, + scope, + rl, + node_datas[node].lhs, + main_tokens[node], + .is_null_ptr, + .optional_payload_unsafe_ptr, + undefined, + node_datas[node].rhs, + null, + ), + else => return orelseCatchExpr( + mod, + scope, + rl, + node_datas[node].lhs, + main_tokens[node], + .is_null, + .optional_payload_unsafe, + undefined, + node_datas[node].rhs, + null, + ), + }, + .@"break" => return breakExpr(mod, scope, rl, node), + .@"continue" => return continueExpr(mod, scope, rl, node), + .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs), + .array_type => return arrayType(mod, scope, rl, node), + .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node), + .char_literal => return charLiteral(mod, scope, rl, node), + .error_set_decl => return errorSetDecl(mod, scope, rl, node), .array_access => return arrayAccess(mod, scope, rl, node), - .slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node)), - .@"catch" => return catchExpr(mod, scope, rl, node), - .@"comptime" => return comptimeKeyword(mod, scope, rl, node), - .@"orelse" => return orelseExpr(mod, scope, rl, node), - .@"switch" => return switchExpr(mod, scope, rl, node), - .ContainerDecl => return containerDecl(mod, scope, rl, node), + .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), + .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), - .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}), - .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), - .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), - .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), - .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), } } -fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.@"comptime") InnerError!*zir.Inst { - const tracy = trace(@src()); - defer tracy.end(); - - return comptimeExpr(mod, scope, rl, node.expr); -} - pub fn comptimeExpr( mod: *Module, parent_scope: *Scope, @@ -493,7 +573,12 @@ pub fn comptimeExpr( return &block.base; } -fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { +fn breakExpr( + mod: *Module, + parent_scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, +) InnerError!*zir.Inst { const tree = parent_scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); @@ -524,9 +609,10 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro }; if (rhs == 0) { - return addZirInstTag(mod, parent_scope, src, .break_void, .{ + const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{ .block = block_inst, }); + return rvalue(mod, parent_scope, rl, result); } gen_zir.break_count += 1; const prev_rvalue_rl_count = gen_zir.rvalue_rl_count; @@ -547,7 +633,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); } } - return br; + return rvalue(mod, parent_scope, rl, br); }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, @@ -561,7 +647,12 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro } } -fn continueExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { +fn continueExpr( + mod: *Module, + parent_scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, +) InnerError!*zir.Inst { const tree = parent_scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); @@ -590,9 +681,10 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerE continue; } - return addZirInstTag(mod, parent_scope, src, .break_void, .{ + const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{ .block = continue_block, }); + return rvalue(mod, parent_scope, rl, result); }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, @@ -1083,12 +1175,6 @@ fn negation( return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); } -fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.slice_type) InnerError!*zir.Inst { - const tree = scope.tree(); - const src = token_starts[node.op_token]; - return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice); -} - fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst { const tree = scope.tree(); const src = token_starts[node.op_token]; @@ -1146,70 +1232,54 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); } -fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.array_type) !*zir.Inst { +fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { const tree = scope.tree(); - const src = token_starts[node.op_token]; + const main_tokens = tree.nodes.items(.main_token); + const node_datas = tree.nodes.items(.data); + const src = token_starts[main_tokens[node]]; const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type), }); + const len_node = node_datas[node].lhs; + const elem_node = node_datas[node].rhs; + if (len_node == 0) { + const elem_type = try typeExpr(mod, scope, elem_node); + const result = try addZIRUnOp(mod, scope, src, .mut_slice_type, elem_type); + return rvalue(mod, scope, rl, result); + } else { + // TODO check for [_]T + const len = try expr(mod, scope, .{ .ty = usize_type }, len_node); + const elem_type = try typeExpr(mod, scope, elem_node); - // TODO check for [_]T - const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); - const elem_type = try typeExpr(mod, scope, node.rhs); - - return addZIRBinOp(mod, scope, src, .array_type, len, elem_type); + const result = try addZIRBinOp(mod, scope, src, .array_type, len, elem_type); + return rvalue(mod, scope, rl, result); + } } -fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.array_type_sentinel) !*zir.Inst { +fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { const tree = scope.tree(); - const src = token_starts[node.op_token]; + const main_tokens = tree.nodes.items(.main_token); + const len_node = node_datas[node].lhs; + const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); + const src = token_starts[main_tokens[node]]; const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type), }); // TODO check for [_]T - const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); - const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); - const elem_type = try typeExpr(mod, scope, node.rhs); + const len = try expr(mod, scope, .{ .ty = usize_type }, len_node); + const sentinel_uncasted = try expr(mod, scope, .none, extra.sentinel); + const elem_type = try typeExpr(mod, scope, extra.elem_type); const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); - return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ + const result = try addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ .len = len, .sentinel = sentinel, .elem_type = elem_type, }, .{}); -} - -fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.anyframe_type) InnerError!*zir.Inst { - const tree = scope.tree(); - const src = token_starts[node.anyframe_token]; - if (node.result) |some| { - const return_type = try typeExpr(mod, scope, some.return_type); - return addZIRUnOp(mod, scope, src, .anyframe_type, return_type); - } else { - return addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.anyframe_type), - }); - } -} - -fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { - const tree = scope.tree(); - const src = token_starts[node.op_token]; - const error_set = try typeExpr(mod, scope, node.lhs); - const payload = try typeExpr(mod, scope, node.rhs); - return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload); -} - -fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.enum_literal) !*zir.Inst { - const tree = scope.tree(); - const src = token_starts[node.name]; - const name = try mod.identifierTokenString(scope, node.name); - - return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); + return rvalue(mod, scope, rl, result); } fn containerField( @@ -1394,85 +1464,50 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con } } -fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.error_set_decl) InnerError!*zir.Inst { +fn errorSetDecl( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, +) InnerError!*zir.Inst { const tree = scope.tree(); - const src = token_starts[node.error_token]; - const decls = node.decls(); - const fields = try scope.arena().alloc([]const u8, decls.len); - - for (decls) |decl, i| { - const tag = decl.castTag(.ErrorTag).?; - fields[i] = try mod.identifierTokenString(scope, tag.name_token); - } - - return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); -} + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); -fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { - const tree = scope.tree(); - const src = token_starts[node.token]; - return addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.anyerror_type), - }); -} + // Count how many fields there are. + const error_token = main_tokens[node]; + const count: usize = count: { + var tok_i = error_token + 2; + var count: usize = 0; + while (true) : (tok_i += 1) { + switch (token_tags[tok_i]) { + .doc_comment, .comma => {}, + .identifier => count += 1, + .r_paren => break :count count, + else => unreachable, + } + } else unreachable; // TODO should not need else unreachable here + }; -fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.@"catch") InnerError!*zir.Inst { - switch (rl) { - .ref => return orelseCatchExpr( - mod, - scope, - rl, - node.lhs, - node.op_token, - .is_err_ptr, - .err_union_payload_unsafe_ptr, - .err_union_code_ptr, - node.rhs, - node.payload, - ), - else => return orelseCatchExpr( - mod, - scope, - rl, - node.lhs, - node.op_token, - .is_err, - .err_union_payload_unsafe, - .err_union_code, - node.rhs, - node.payload, - ), - } -} - -fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { - switch (rl) { - .ref => return orelseCatchExpr( - mod, - scope, - rl, - node.lhs, - node.op_token, - .is_null_ptr, - .optional_payload_unsafe_ptr, - undefined, - node.rhs, - null, - ), - else => return orelseCatchExpr( - mod, - scope, - rl, - node.lhs, - node.op_token, - .is_null, - .optional_payload_unsafe, - undefined, - node.rhs, - null, - ), + const fields = try scope.arena().alloc([]const u8, count); + { + var tok_i = error_token + 2; + var field_i: usize = 0; + while (true) : (tok_i += 1) { + switch (token_tags[tok_i]) { + .doc_comment, .comma => {}, + .identifier => { + fields[field_i] = try mod.identifierTokenString(scope, tok_i); + field_i += 1; + }, + .r_paren => break, + else => unreachable, + } + } } + const src = token_starts[error_token]; + const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); + return rvalue(mod, scope, rl, result); } fn orelseCatchExpr( @@ -1681,55 +1716,78 @@ pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) I } } -fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.array_access) InnerError!*zir.Inst { +fn arrayAccess( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, +) InnerError!*zir.Inst { const tree = scope.tree(); - const src = token_starts[node.rtoken]; + const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const src = token_starts[main_tokens[node]]; const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type), }); const index_rl: ResultLoc = .{ .ty = usize_type }; - - if (rl == .ref) { - return addZirInstTag(mod, scope, src, .elem_ptr, .{ - .array = try expr(mod, scope, .ref, node.lhs), - .index = try expr(mod, scope, index_rl, node.index_expr), - }); + switch (rl) { + .ref => return addZirInstTag(mod, scope, src, .elem_ptr, .{ + .array = try expr(mod, scope, .ref, node_datas[node].lhs), + .index = try expr(mod, scope, index_rl, node_datas[node].rhs), + }), + else => return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{ + .array = try expr(mod, scope, .none, node_datas[node].lhs), + .index = try expr(mod, scope, index_rl, node_datas[node].rhs), + })), } - return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{ - .array = try expr(mod, scope, .none, node.lhs), - .index = try expr(mod, scope, index_rl, node.index_expr), - })); } -fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.slice) InnerError!*zir.Inst { +fn sliceExpr( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + slice: ast.full.Slice, +) InnerError!*zir.Inst { const tree = scope.tree(); - const src = token_starts[node.rtoken]; + const token_starts = tree.tokens.items(.start); + const src = token_starts[slice.ast.lbracket]; const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type), }); - const array_ptr = try expr(mod, scope, .ref, node.lhs); - const start = try expr(mod, scope, .{ .ty = usize_type }, node.start); + const array_ptr = try expr(mod, scope, .ref, slice.ast.sliced); + const start = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.start); - if (node.end == null and node.sentinel == null) { - return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start); + if (slice.ast.sentinel == 0) { + if (slice.ast.end == 0) { + const result = try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start); + return rvalue(mod, scope, rl, result); + } else { + const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end); + // TODO a ZIR slice_open instruction + const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{ + .array_ptr = array_ptr, + .start = start, + }, .{ .end = end }); + return rvalue(mod, scope, rl, result); + } } - const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null; - // we could get the child type here, but it is easier to just do it in semantic analysis. - const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null; - - return try addZIRInst( - mod, - scope, - src, - zir.Inst.Slice, - .{ .array_ptr = array_ptr, .start = start }, - .{ .end = end, .sentinel = sentinel }, - ); + const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end); + // TODO pass the proper result loc to this expression using a ZIR instruction + // "get the child element type for a slice target". + const sentinel = try expr(mod, scope, .none, slice.ast.sentinel); + const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{ + .array_ptr = array_ptr, + .start = start, + }, .{ + .end = end, + .sentinel = sentinel, + }); + return rvalue(mod, scope, rl, result); } fn simpleBinOp( @@ -2070,7 +2128,6 @@ fn whileExpr( }; defer then_scope.instructions.deinit(mod.gpa); - // declare payload to the then_scope const then_sub_scope = &then_scope.base; loop_scope.break_count += 1; @@ -2101,7 +2158,7 @@ fn whileExpr( if (loop_scope.label) |some| { if (!some.used) { - return mod.fail(scope, token_starts[some.token], "unused while label", .{}); + return mod.fail(scope, token_starts[some.token], "unused while loop label", .{}); } } return finishThenElseBlock( @@ -2126,20 +2183,21 @@ fn forExpr( mod: *Module, scope: *Scope, rl: ResultLoc, - for_node: *ast.Node.@"for", + for_full: ast.full.While, ) InnerError!*zir.Inst { - if (for_node.label) |label| { - try checkLabelRedefinition(mod, scope, label); + if (for_full.label_token) |label_token| { + try checkLabelRedefinition(mod, scope, label_token); } - if (for_node.inline_token) |tok| - return mod.failTok(scope, tok, "TODO inline for", .{}); + if (for_full.inline_token) |inline_token| { + return mod.failTok(scope, inline_token, "TODO inline for", .{}); + } - // setup variables and constants + // Set up variables and constants. const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); - const for_src = token_starts[for_node.for_token]; + const for_src = token_starts[for_full.ast.while_token]; const index_ptr = blk: { const usize_type = try addZIRInstConst(mod, scope, for_src, .{ .ty = Type.initTag(.type), @@ -2154,8 +2212,8 @@ fn forExpr( _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero); break :blk index_ptr; }; - const array_ptr = try expr(mod, scope, .ref, for_node.array_expr); - const cond_src = token_starts[for_node.array_expr.firstToken()]; + const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr); + const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)]; const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr); var loop_scope: Scope.GenZIR = .{ @@ -2217,15 +2275,15 @@ fn forExpr( }); loop_scope.break_block = for_block; loop_scope.continue_block = cond_block; - if (for_node.label) |some| { + if (for_full.label_token) |label_token| { loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ - .token = some, + .token = label_token, .block_inst = for_block, }); } // while body - const then_src = token_starts[for_node.body.lastToken()]; + const then_src = token_starts[tree.lastToken(for_full.ast.then_expr)]; var then_scope: Scope.GenZIR = .{ .parent = &cond_scope.base, .decl = cond_scope.decl, @@ -2237,23 +2295,27 @@ fn forExpr( var index_scope: Scope.LocalPtr = undefined; const then_sub_scope = blk: { - const payload = for_node.payload.castTag(.PointerIndexPayload).?; - const is_ptr = payload.ptr_token != null; - const value_name = tree.tokenSlice(payload.value_symbol.firstToken()); + const payload_token = for_full.payload_token.?; + const ident = if (token_tags[payload_token] == .asterisk) + payload_token + 1 + else + payload_token; + const is_ptr = ident != payload_token; + const value_name = tree.tokenSlice(ident); if (!mem.eql(u8, value_name, "_")) { - return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{}); + return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{}); } else if (is_ptr) { - return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); + return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{}); } - const index_symbol_node = payload.index_symbol orelse - break :blk &then_scope.base; - - const index_name = tree.tokenSlice(tree.firstToken(index_symbol_node)); - if (mem.eql(u8, index_name, "_")) { + const index_token = if (token_tags[ident + 1] == .comma) + ident + 2 + else break :blk &then_scope.base; + if (mem.eql(u8, tree.tokenSlice(index_token), "_")) { + return mod.failTok(&then_scope.base, index_token, "discard of index capture not allowed; omit it instead", .{}); } - // TODO make this const without an extra copy? + const index_name = try mod.identifierTokenString(&then_scope.base, index_token); index_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -2264,7 +2326,7 @@ fn forExpr( }; loop_scope.break_count += 1; - const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_node.body); + const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); // else branch var else_scope: Scope.GenZIR = .{ @@ -2276,18 +2338,23 @@ fn forExpr( }; defer else_scope.instructions.deinit(mod.gpa); - var else_src: usize = undefined; - const else_result: ?*zir.Inst = if (for_node.@"else") |else_node| blk: { - else_src = token_starts[else_node.body.lastToken()]; + const else_node = for_full.ast.else_expr; + const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: { loop_scope.break_count += 1; - break :blk try expr(mod, &else_scope.base, loop_scope.break_result_loc, else_node.body); - } else blk: { - else_src = token_starts[for_node.lastToken()]; - break :blk null; - }; + const sub_scope = &else_scope.base; + break :blk .{ + .src = token_starts[tree.lastToken(else_node)], + .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), + }; + } else + .{ + .src = token_starts[tree.lastToken(then_node)], + .result = null, + }; + if (loop_scope.label) |some| { if (!some.used) { - return mod.fail(scope, token_starts[some.token], "unused for label", .{}); + return mod.fail(scope, token_starts[some.token], "unused for loop label", .{}); } } return finishThenElseBlock( @@ -2300,41 +2367,46 @@ fn forExpr( &condbr.positionals.then_body, &condbr.positionals.else_body, then_src, - else_src, + else_info.src, then_result, - else_result, + else_info.result, for_block, cond_block, ); } -fn switchCaseUsesRef(node: *ast.Node.@"switch") bool { - for (node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.switch_case).?; - const uncasted_payload = case.payload orelse continue; - const payload = uncasted_payload.castTag(.PointerPayload).?; - if (payload.ptr_token) |_| return true; - } - return false; -} - -fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { - var cur = node; +fn getRangeNode( + node_tags: []const ast.Node.Tag, + node_datas: []const ast.Node.Data, + start_node: ast.Node.Index, +) ?ast.Node.Index { + var node = start_node; while (true) { - switch (cur.tag) { - .range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), - .grouped_expression => cur = @fieldParentPtr(ast.Node.grouped_expression, "base", cur).expr, + switch (node_tags[node]) { + .switch_range => return node, + .grouped_expression => node = node_datas[node].lhs, else => return null, } } } -fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.@"switch") InnerError!*zir.Inst { +fn switchExpr( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + switch_node: ast.Node.Index, +) InnerError!*zir.Inst { const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); - const switch_src = token_starts[switch_node.switch_token]; - const use_ref = switchCaseUsesRef(switch_node); + const token_tags = tree.tokens.items(.tag); + + const switch_token = main_tokens[switch_node]; + const target_node = datas[switch_node].lhs; + const extra = tree.extraData(datas[switch_node].rhs, ast.switch_node.SubRange); + const case_nodes = tree.extra_data[extra.start..extra.end]; + + const switch_src = token_starts[switch_token]; var block_scope: Scope.GenZIR = .{ .parent = scope, @@ -2349,18 +2421,26 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node var items = std.ArrayList(*zir.Inst).init(mod.gpa); defer items.deinit(); - // first we gather all the switch items and check else/'_' prongs + // First we gather all the switch items and check else/'_' prongs. var else_src: ?usize = null; var underscore_src: ?usize = null; var first_range: ?*zir.Inst = null; var simple_case_count: usize = 0; - for (switch_node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.switch_case).?; - const case_src = token_starts[case.firstToken()]; - assert(case.items_len != 0); - + var any_payload_is_ref = false; + for (case_nodes) |case_node| { + const case = switch (node_tags[case_node]) { + .switch_case_one => tree.switchCaseOne(case_node), + .switch_case => tree.switchCase(case_node), + else => unreachable, + }; + if (case.payload_token) |payload_token| { + if (token_tags[payload_token] == .asterisk) { + any_payload_is_ref = true; + } + } // Check for else/_ prong, those are handled last. - if (case.items_len == 1 and case.items()[0].tag == .switch_else) { + if (case.ast.values.len == 0) { + const case_src = token_starts[case.ast.arrow_token - 1]; if (else_src) |src| { const msg = msg: { const msg = try mod.errMsg( @@ -2377,9 +2457,11 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node } else_src = case_src; continue; - } else if (case.items_len == 1 and case.items()[0].tag == .identifier and - mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) + } else if (case.ast.values.len == 1 and + node_tags[case.ast.values[0]] == .identifier and + mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) { + const case_src = token_starts[case.ast.arrow_token - 1]; if (underscore_src) |src| { const msg = msg: { const msg = try mod.errMsg( @@ -2416,14 +2498,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node } } - if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) simple_case_count += 1; + if (case.ast.values.len == 1 and + getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) + { + simple_case_count += 1; + } - // generate all the switch items as comptime expressions - for (case.items()) |item| { - if (getRangeNode(item)) |range| { - const start = try comptimeExpr(mod, &block_scope.base, .none, range.lhs); - const end = try comptimeExpr(mod, &block_scope.base, .none, range.rhs); - const range_src = token_starts[range.op_token]; + // Generate all the switch items as comptime expressions. + for (case.ast.values) |item| { + if (getRangeNode(node_tags, node_datas, item)) |range| { + const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs); + const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs); + const range_src = token_starts[main_tokens[range]]; const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end); try items.append(range_inst); } else { @@ -2438,21 +2524,25 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node if (underscore_src != null) special_prong = .underscore; var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count); - const target_ptr = if (use_ref) try expr(mod, &block_scope.base, .ref, switch_node.expr) else null; - const target = if (target_ptr) |some| - try addZIRUnOp(mod, &block_scope.base, some.src, .deref, some) + const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) + .{ + .rl = .ref, + .tag = .switchbr_ref, + } else - try expr(mod, &block_scope.base, .none, switch_node.expr); - const switch_inst = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ + .{ + .rl = .none, + .tag = .switchbr, + }; + const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node); + const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{ .target = target, .cases = cases, .items = try block_scope.arena.dupe(*zir.Inst, items.items), .else_body = undefined, // populated below - }, .{ .range = first_range, .special_prong = special_prong, }); - const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); @@ -2475,29 +2565,35 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node }; defer else_scope.instructions.deinit(mod.gpa); - // Now generate all but the special cases - var special_case: ?*ast.Node.switch_case = null; + // Now generate all but the special cases. + var special_case: ?ast.Node.Index = null; var items_index: usize = 0; var case_index: usize = 0; - for (switch_node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.switch_case).?; - const case_src = token_starts[case.firstToken()]; - // reset without freeing to reduce allocations. - case_scope.instructions.items.len = 0; + for (case_nodes) |case_node| { + const case = switch (node_tags[case_node]) { + .switch_case_one => tree.switchCaseOne(case_node), + .switch_case => tree.switchCase(case_node), + else => unreachable, + }; + const case_src = token_starts[main_tokens[case_node]]; + case_scope.instructions.shrinkRetainingCapacity(0); // Check for else/_ prong, those are handled last. - if (case.items_len == 1 and case.items()[0].tag == .switch_else) { + if (case.ast.values.len == 0) { special_case = case; continue; - } else if (case.items_len == 1 and case.items()[0].tag == .identifier and - mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) + } else if (case.ast.values.len == 1 and + node_tags[case.ast.values[0]] == .identifier and + mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) { special_case = case; continue; } // If this is a simple one item prong then it is handled by the switchbr. - if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) { + if (case.ast.values.len == 1 and + getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) + { const item = items.items[items_index]; items_index += 1; try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); @@ -2510,16 +2606,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node continue; } - // TODO if the case has few items and no ranges it might be better - // to just handle them as switch prongs. - // Check if the target matches any of the items. // 1, 2, 3..6 will result in // target == 1 or target == 2 or (target >= 3 and target <= 6) + // TODO handle multiple items as switch prongs rather than along with ranges. var any_ok: ?*zir.Inst = null; - for (case.items()) |item| { - if (getRangeNode(item)) |range| { - const range_src = token_starts[range.op_token]; + for (case.ast.values) |item| { + if (getRangeNode(node_tags, node_datas, item)) |range| { + const range_src = token_starts[main_tokens[range]]; const range_inst = items.items[items_index].castTag(.switch_range).?; items_index += 1; @@ -2580,7 +2674,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node // Not handling all possible cases is a compile error. _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); } - switch_inst.castTag(.switchbr).?.positionals.else_body = .{ + switch_inst.positionals.else_body = .{ .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), }; @@ -2592,19 +2686,22 @@ fn switchCaseExpr( scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, - case: *ast.Node.switch_case, + case: ast.full.SwitchCase, target: *zir.Inst, target_ptr: ?*zir.Inst, ) !void { const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); - const case_src = token_starts[case.firstToken()]; + const case_src = token_starts[case.ast.arrow_token]; const sub_scope = blk: { - const uncasted_payload = case.payload orelse break :blk scope; - const payload = uncasted_payload.castTag(.PointerPayload).?; - const is_ptr = payload.ptr_token != null; - const value_name = tree.tokenSlice(payload.value_symbol.firstToken()); + const payload_token = case.payload_token orelse break :blk scope; + const ident = if (token_tags[payload_token] == .asterisk) + payload_token + 1 + else + payload_token; + const is_ptr = ident != payload_token; + const value_name = tree.tokenSlice(ident); if (mem.eql(u8, value_name, "_")) { if (is_ptr) { return mod.failTok(scope, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); @@ -2614,7 +2711,7 @@ fn switchCaseExpr( return mod.failNode(scope, payload.value_symbol, "TODO implement switch value payload", .{}); }; - const case_body = try expr(mod, sub_scope, rl, case.expr); + const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr); if (!case_body.tag.isNoReturn()) { _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{ .block = block, @@ -2820,12 +2917,13 @@ fn multilineStringLiteral( return rvalue(mod, scope, rl, str_inst); } -fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst { +fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); - const src = token_starts[node.token]; - const slice = tree.tokenSlice(node.token); + const main_token = main_tokens[node]; + const src = token_starts[main_token]; + const slice = tree.tokenSlice(main_token); var bad_index: usize = undefined; const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { @@ -2834,11 +2932,11 @@ fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte}); }, }; - - return addZIRInstConst(mod, scope, src, .{ + const result = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.comptime_int), .val = try Value.Tag.int_u64.create(scope.arena(), value), }); + return rvalue(mod, scope, rl, result); } fn integerLiteral( @@ -3675,6 +3773,29 @@ pub fn addZirInstTag( return &inst.base; } +pub fn addZirInstT( + mod: *Module, + scope: *Scope, + src: usize, + comptime T: type, + tag: zir.Inst.Tag, + positionals: std.meta.fieldInfo(tag.Type(), .positionals).field_type, +) !*T { + const gen_zir = scope.getGenZIR(); + try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); + const inst = try gen_zir.arena.create(T); + inst.* = .{ + .base = .{ + .tag = tag, + .src = src, + }, + .positionals = positionals, + .kw_args = .{}, + }; + gen_zir.instructions.appendAssumeCapacity(&inst.base); + return inst; +} + pub fn addZIRInstSpecial( mod: *Module, scope: *Scope, diff --git a/src/zir.zig b/src/zir.zig index fc68aee216..fcbcee9ccd 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -343,6 +343,8 @@ pub const Inst = struct { void_value, /// A switch expression. switchbr, + /// Same as `switchbr` but the target is a pointer to the value being switched on. + switchbr_ref, /// A range in a switch case, `lhs...rhs`. /// Only checks that `lhs >= rhs` if they are ints, everything else is /// validated by the .switch instruction. @@ -453,6 +455,8 @@ pub const Inst = struct { .block_comptime_flat, => Block, + .switchbr, .switchbr_ref => SwitchBr, + .arg => Arg, .array_type_sentinel => ArrayTypeSentinel, .@"break" => Break, @@ -488,7 +492,6 @@ pub const Inst = struct { .enum_type => EnumType, .union_type => UnionType, .struct_type => StructType, - .switchbr => SwitchBr, }; } @@ -617,7 +620,6 @@ pub const Inst = struct { .struct_type, .void_value, .switch_range, - .switchbr, => false, .@"break", @@ -632,6 +634,8 @@ pub const Inst = struct { .container_field_named, .container_field_typed, .container_field, + .switchbr, + .switchbr_ref, => true, }; } @@ -730,6 +734,8 @@ pub const Inst = struct { kw_args: struct {}, }; + // TODO break this into multiple call instructions to avoid paying the cost + // of the calling convention field most of the time. pub const Call = struct { pub const base_tag = Tag.call; base: Inst, @@ -737,10 +743,9 @@ pub const Inst = struct { positionals: struct { func: *Inst, args: []*Inst, - }, - kw_args: struct { modifier: std.builtin.CallOptions.Modifier = .auto, }, + kw_args: struct {}, }; pub const DeclRef = struct { @@ -1185,7 +1190,6 @@ pub const Inst = struct { }; pub const SwitchBr = struct { - pub const base_tag = Tag.switchbr; base: Inst, positionals: struct { @@ -1194,14 +1198,12 @@ pub const Inst = struct { items: []*Inst, cases: []Case, else_body: Body, - }, - kw_args: struct { /// Pointer to first range if such exists. range: ?*Inst = null, special_prong: SpecialProng = .none, }, + kw_args: struct {}, - // Not anonymous due to stage1 limitations pub const SpecialProng = enum { none, @"else", diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 480e0b4c33..80146397c5 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -154,7 +154,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), .void_value => return mod.constVoid(scope, old_inst.src), - .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?), + .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false), + .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, true), .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), .container_field_named, @@ -1554,10 +1555,15 @@ fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError return mod.constVoid(scope, inst.base.src); } -fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { +fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr, ref: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - const target = try resolveInst(mod, scope, inst.positionals.target); + + const target_ptr = try resolveInst(mod, scope, inst.positionals.target); + const target = if (ref) + try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target.src) + else + target_ptr; try validateSwitch(mod, scope, target, inst); if (try mod.resolveDefinedValue(scope, target)) |target_val| { -- cgit v1.2.3 From 5a2620fcca55813d87000f3018e70509b1d325e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Feb 2021 22:22:10 -0700 Subject: stage2: fix some of the compilation errors in this branch --- src/Module.zig | 42 ++++++++++++++++++++++++------------- src/astgen.zig | 33 ++++++++++++++--------------- src/zir.zig | 25 +++++++++++++++++----- src/zir_sema.zig | 64 +++++++++++++++++++++++++++++++++++++++++++------------- 4 files changed, 114 insertions(+), 50 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 19566dee43..17084677d4 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1223,14 +1223,20 @@ fn astgenAndSemaFn( .{}, ); } - if (fn_proto.ast.callconv_expr != 0) { - return mod.failNode( - &fn_type_scope.base, - fn_proto.ast.callconv_expr, - "TODO implement function calling convention expression", - .{}, - ); - } + const opt_cc: ?*zir.Inst = if (fn_proto.ast.callconv_expr != 0) cc: { + // TODO instead of enum literal type, this needs to be the + // std.builtin.CallingConvention enum. We need to implement importing other files + // and enums in order to fix this. + const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)]; + const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.enum_literal_type), + }); + break :cc try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ + .ty = enum_lit_ty, + }, fn_proto.ast.callconv_expr); + } else null; + const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; if (token_tags[maybe_bang] == .bang) { return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); @@ -1241,10 +1247,17 @@ fn astgenAndSemaFn( type_type_rl, fn_proto.ast.return_type, ); - const fn_type_inst = try astgen.addZIRInst(mod, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ - .return_type = return_type_inst, - .param_types = param_types, - }, .{}); + const fn_type_inst = if (opt_cc) |cc| + try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ + .return_type = return_type_inst, + .param_types = param_types, + .cc = cc, + }) + else + try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ + .return_type = return_type_inst, + .param_types = param_types, + }); if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; @@ -1316,6 +1329,7 @@ fn astgenAndSemaFn( .decl = decl, .arena = &decl_arena.allocator, .parent = &decl.container.base, + .force_comptime = false, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -1348,7 +1362,7 @@ fn astgenAndSemaFn( params_scope = &sub_scope.base; } - try astgen.blockExpr(mod, params_scope, body_node); + try astgen.expr(mod, params_scope, .none, body_node); if (gen_scope.instructions.items.len == 0 or !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) @@ -1496,7 +1510,7 @@ fn astgenAndSemaVarDecl( assert(is_extern); return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{}); } - const is_mutable = token_tags[var_decl.mut_token] == .keyword_var; + const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: { if (!is_mutable) { return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); diff --git a/src/astgen.zig b/src/astgen.zig index aef1b21a6c..125e9bceda 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -539,17 +539,6 @@ pub fn comptimeExpr( } const tree = parent_scope.tree(); - const main_tokens = tree.nodes.items(.main_token); - const token_tags = tree.tokens.items(.tag); - - // Optimization for labeled blocks: don't need to have 2 layers of blocks, - // we can reuse the existing one. - const lbrace = main_tokens[node]; - if (token_tags[lbrace - 1] == .colon and - token_tags[lbrace - 2] == .identifier) - { - return labeledBlockExpr(mod, parent_scope, rl, node, .block_comptime); - } // Make a scope to collect generated instructions in the sub-expression. var block_scope: Scope.GenZIR = .{ @@ -708,9 +697,13 @@ pub fn blockExpr( const tracy = trace(@src()); defer tracy.end(); + const tree = scope.tree(); + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); + const lbrace = main_tokens[node]; if (token_tags[lbrace - 1] == .colon) { - return labeledBlockExpr(mod, scope, rl, block_node, .block); + return labeledBlockExpr(mod, scope, rl, block_node, statements, .block); } try blockExprStmts(mod, scope, block_node, statements); @@ -766,7 +759,8 @@ fn labeledBlockExpr( mod: *Module, parent_scope: *Scope, rl: ResultLoc, - block_node: *ast.Node.labeled_block, + block_node: ast.Node.Index, + statements: []const ast.Node.Index, zir_tag: zir.Inst.Tag, ) InnerError!*zir.Inst { const tracy = trace(@src()); @@ -777,9 +771,14 @@ fn labeledBlockExpr( const tree = parent_scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); - const src = token_starts[block_node.lbrace]; + const token_starts = tree.tokens.items(.start); + + const lbrace = main_tokens[block_node]; + const label_token = lbrace - 1; + assert(token_tags[label_token] == .identifier); + const src = token_starts[lbrace]; - try checkLabelRedefinition(mod, parent_scope, block_node.label); + try checkLabelRedefinition(mod, parent_scope, label_token); // Create the Block ZIR instruction so that we can put it into the GenZIR struct // so that break statements can reference it. @@ -804,7 +803,7 @@ fn labeledBlockExpr( .instructions = .{}, // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ - .token = block_node.label, + .token = label_token, .block_inst = block_inst, }), }; @@ -813,7 +812,7 @@ fn labeledBlockExpr( defer block_scope.labeled_breaks.deinit(mod.gpa); defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); - try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); + try blockExprStmts(mod, &block_scope.base, block_node, block_node.statements()); if (!block_scope.label.?.used) { return mod.fail(parent_scope, token_starts[block_node.label], "unused block label", .{}); diff --git a/src/zir.zig b/src/zir.zig index fcbcee9ccd..9a3c080760 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -172,8 +172,10 @@ pub const Inst = struct { floatcast, /// Declare a function body. @"fn", - /// Returns a function type. - fntype, + /// Returns a function type, assuming unspecified calling convention. + fn_type, + /// Returns a function type, with a calling convention instruction operand. + fn_type_cc, /// @import(operand) import, /// Integer literal. @@ -478,7 +480,8 @@ pub const Inst = struct { .@"export" => Export, .param_type => ParamType, .primitive => Primitive, - .fntype => FnType, + .fn_type => FnType, + .fn_type_cc => FnTypeCc, .elem_ptr, .elem_val => Elem, .condbr => CondBr, .ptr_type => PtrType, @@ -552,7 +555,8 @@ pub const Inst = struct { .field_ptr_named, .field_val_named, .@"fn", - .fntype, + .fn_type, + .fn_type_cc, .int, .intcast, .int_type, @@ -877,7 +881,18 @@ pub const Inst = struct { }; pub const FnType = struct { - pub const base_tag = Tag.fntype; + pub const base_tag = Tag.fn_type; + base: Inst, + + positionals: struct { + param_types: []*Inst, + return_type: *Inst, + }, + kw_args: struct {}, + }; + + pub const FnTypeCc = struct { + pub const base_tag = Tag.fn_type_cc; base: Inst, positionals: struct { diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 80146397c5..1a2e99ded5 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -91,7 +91,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?), .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?), .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?), - .fntype => return zirFnType(mod, scope, old_inst.castTag(.fntype).?), + .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?), + .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?), .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?), .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?), .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?), @@ -155,7 +156,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), .void_value => return mod.constVoid(scope, old_inst.src), .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false), - .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, true), + .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true), .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), .container_field_named, @@ -958,11 +959,11 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { ); } - if (inst.kw_args.modifier == .compile_time) { + if (inst.positionals.modifier == .compile_time) { return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{}); } - if (inst.kw_args.modifier != .auto) { - return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier}); + if (inst.positionals.modifier != .auto) { + return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.positionals.modifier}); } // TODO handle function calls of generic functions @@ -1295,34 +1296,69 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - const return_type = try resolveType(mod, scope, fntype.positionals.return_type); + + return fnTypeCommon( + mod, + scope, + &fntype.base, + fntype.positionals.param_types, + fntype.positionals.return_type, + .Unspecified, + ); +} + +fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc); + // TODO once we're capable of importing and analyzing decls from + // std.builtin, this needs to change const cc_str = cc_tv.val.castTag(.enum_literal).?.data; const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str}); + return fnTypeCommon( + mod, + scope, + &fntype.base, + fntype.positionals.param_types, + fntype.positionals.return_type, + cc, + ); +} + +fn fnTypeCommon( + mod: *Module, + scope: *Scope, + zir_inst: *zir.Inst, + zir_param_types: []*zir.Inst, + zir_return_type: *zir.Inst, + cc: std.builtin.CallingConvention, +) InnerError!*Inst { + const return_type = try resolveType(mod, scope, zir_return_type); // Hot path for some common function types. - if (fntype.positionals.param_types.len == 0) { + if (zir_param_types.len == 0) { if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { - return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); + return mod.constType(scope, zir_inst.src, Type.initTag(.fn_noreturn_no_args)); } if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { - return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); + return mod.constType(scope, zir_inst.src, Type.initTag(.fn_void_no_args)); } if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { - return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); + return mod.constType(scope, zir_inst.src, Type.initTag(.fn_naked_noreturn_no_args)); } if (return_type.zigTypeTag() == .Void and cc == .C) { - return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); + return mod.constType(scope, zir_inst.src, Type.initTag(.fn_ccc_void_no_args)); } } const arena = scope.arena(); - const param_types = try arena.alloc(Type, fntype.positionals.param_types.len); - for (fntype.positionals.param_types) |param_type, i| { + const param_types = try arena.alloc(Type, zir_param_types.len); + for (zir_param_types) |param_type, i| { const resolved = try resolveType(mod, scope, param_type); // TODO skip for comptime params if (!resolved.isValidVarType(false)) { @@ -1336,7 +1372,7 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* .return_type = return_type, .cc = cc, }); - return mod.constType(scope, fntype.base.src, fn_ty); + return mod.constType(scope, zir_inst.src, fn_ty); } fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { -- cgit v1.2.3 From 29daf10639149bd023db0be4e04eaf154dce0f83 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Feb 2021 22:34:06 -0700 Subject: stage2: fix a couple more compilation errors --- lib/std/zig/ast.zig | 4 ++-- src/Module.zig | 39 +++++++++++++++++++-------------------- src/astgen.zig | 17 ++--------------- src/zir_sema.zig | 18 +++++++++--------- 4 files changed, 32 insertions(+), 46 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 02bca79986..72b47ecd3f 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -2054,7 +2054,7 @@ pub const full = struct { return null; } const param_type = it.fn_proto.ast.params[it.param_i]; - var tok_i = tree.firstToken(param_type) - 1; + var tok_i = it.tree.firstToken(param_type) - 1; while (true) : (tok_i -= 1) switch (token_tags[tok_i]) { .colon => continue, .identifier => name_token = tok_i, @@ -2063,7 +2063,7 @@ pub const full = struct { else => break, }; it.param_i += 1; - it.tok_i = tree.lastToken(param_type) + 1; + it.tok_i = it.tree.lastToken(param_type) + 1; it.tok_flag = true; return Param{ .first_doc_comment = first_doc_comment, diff --git a/src/Module.zig b/src/Module.zig index 17084677d4..d2530d7df3 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1223,19 +1223,6 @@ fn astgenAndSemaFn( .{}, ); } - const opt_cc: ?*zir.Inst = if (fn_proto.ast.callconv_expr != 0) cc: { - // TODO instead of enum literal type, this needs to be the - // std.builtin.CallingConvention enum. We need to implement importing other files - // and enums in order to fix this. - const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)]; - const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.enum_literal_type), - }); - break :cc try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ - .ty = enum_lit_ty, - }, fn_proto.ast.callconv_expr); - } else null; const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; if (token_tags[maybe_bang] == .bang) { @@ -1247,13 +1234,24 @@ fn astgenAndSemaFn( type_type_rl, fn_proto.ast.return_type, ); - const fn_type_inst = if (opt_cc) |cc| - try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ + const fn_type_inst = if (fn_proto.ast.callconv_expr != 0) cc: { + // TODO instead of enum literal type, this needs to be the + // std.builtin.CallingConvention enum. We need to implement importing other files + // and enums in order to fix this. + const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)]; + const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.enum_literal_type), + }); + const cc = try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ + .ty = enum_lit_ty, + }, fn_proto.ast.callconv_expr); + break :cc try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ .return_type = return_type_inst, .param_types = param_types, .cc = cc, - }) - else + }); + } else try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ .return_type = return_type_inst, .param_types = param_types, @@ -1362,13 +1360,13 @@ fn astgenAndSemaFn( params_scope = &sub_scope.base; } - try astgen.expr(mod, params_scope, .none, body_node); + _ = try astgen.expr(mod, params_scope, .none, body_node); if (gen_scope.instructions.items.len == 0 or !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) { const src = token_starts[tree.lastToken(body_node)]; - _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .returnvoid); + _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .return_void); } if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { @@ -1542,6 +1540,7 @@ fn astgenAndSemaVarDecl( .decl = decl, .arena = &gen_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -1600,7 +1599,7 @@ fn astgenAndSemaVarDecl( } else if (!is_extern) { return mod.failTok( &block_scope.base, - tree.firstToken(var_decl), + var_decl.ast.mut_token, "variables must be initialized", .{}, ); diff --git a/src/astgen.zig b/src/astgen.zig index 125e9bceda..a018d58d2f 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -539,6 +539,7 @@ pub fn comptimeExpr( } const tree = parent_scope.tree(); + const token_starts = tree.tokens.items(.start); // Make a scope to collect generated instructions in the sub-expression. var block_scope: Scope.GenZIR = .{ @@ -693,7 +694,7 @@ pub fn blockExpr( rl: ResultLoc, block_node: ast.Node.Index, statements: []const ast.Node.Index, -) InnerError!void { +) InnerError!*zir.Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1174,20 +1175,6 @@ fn negation( return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); } -fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst { - const tree = scope.tree(); - const src = token_starts[node.op_token]; - return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) { - .Asterisk, .AsteriskAsterisk => .One, - // TODO stage1 type inference bug - .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) { - .identifier => .C, - else => .Many, - }), - else => unreachable, - }); -} - fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst { const simple = ptr_info.allowzero_token == null and ptr_info.align_info == null and diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 1a2e99ded5..83d7113c9c 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -981,8 +981,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { const ret_type = func.ty.fnReturnType(); const b = try mod.requireFunctionBlock(scope, inst.base.src); - const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time; - const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or + const is_comptime_call = b.is_comptime or inst.positionals.modifier == .compile_time; + const is_inline_call = is_comptime_call or inst.positionals.modifier == .always_inline or func.ty.fnCallingConvention() == .Inline; if (is_inline_call) { const func_val = try mod.resolveConstValue(scope, func); @@ -1668,13 +1668,13 @@ fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr, ref: bool) fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { // validate usage of '_' prongs - if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { + if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); // TODO notes "'_' prong here" inst.positionals.cases[last].src } // check that target type supports ranges - if (inst.kw_args.range) |range_inst| { + if (inst.positionals.range) |range_inst| { switch (target.ty.zigTypeTag()) { .Int, .ComptimeInt => {}, else => { @@ -1725,14 +1725,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw const start = try target.ty.minInt(&arena, mod.getTarget()); const end = try target.ty.maxInt(&arena, mod.getTarget()); if (try range_set.spans(start, end)) { - if (inst.kw_args.special_prong == .@"else") { + if (inst.positionals.special_prong == .@"else") { return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); } return; } } - if (inst.kw_args.special_prong != .@"else") { + if (inst.positionals.special_prong != .@"else") { return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); } }, @@ -1752,15 +1752,15 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw return mod.fail(scope, item.src, "duplicate switch value", .{}); } } - if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") { + if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") { return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); } - if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") { + if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") { return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); } }, .EnumLiteral, .Void, .Fn, .Pointer, .Type => { - if (inst.kw_args.special_prong != .@"else") { + if (inst.positionals.special_prong != .@"else") { return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); } -- cgit v1.2.3 From 9010bd8aec612d5a14e4be800c80b72025fac2c5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Feb 2021 20:09:29 -0700 Subject: stage2: astgen: fix most of the remaining compile errors more progress on converting astgen to the new AST memory layout. only a few code paths left to update. --- lib/std/zig/ast.zig | 40 +- lib/std/zig/render.zig | 48 +-- src/BuiltinFn.zig | 1043 ++++++++++++++++++++++++------------------------ src/Module.zig | 17 +- src/astgen.zig | 699 +++++++++++++++++++++----------- src/zir.zig | 16 +- src/zir_sema.zig | 12 +- 7 files changed, 1052 insertions(+), 823 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 72b47ecd3f..c8f9afd080 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1754,25 +1754,20 @@ pub const Tree = struct { const token_tags = tree.tokens.items(.tag); // TODO: looks like stage1 isn't quite smart enough to handle enum // literals in some places here - const Kind = full.PtrType.Kind; - const kind: Kind = switch (token_tags[info.main_token]) { + const Size = std.builtin.TypeInfo.Pointer.Size; + const size: Size = switch (token_tags[info.main_token]) { .asterisk, .asterisk_asterisk, => switch (token_tags[info.main_token + 1]) { - .r_bracket => .many, - .colon => .sentinel, - .identifier => if (token_tags[info.main_token - 1] == .l_bracket) Kind.c else .one, - else => .one, - }, - .l_bracket => switch (token_tags[info.main_token + 1]) { - .r_bracket => Kind.slice, - .colon => .slice_sentinel, - else => unreachable, + .r_bracket, .colon => .Many, + .identifier => if (token_tags[info.main_token - 1] == .l_bracket) Size.C else .One, + else => .One, }, + .l_bracket => Size.Slice, else => unreachable, }; var result: full.PtrType = .{ - .kind = kind, + .size = size, .allowzero_token = null, .const_token = null, .volatile_token = null, @@ -1782,13 +1777,7 @@ pub const Tree = struct { // here while looking for modifiers as that could result in false // positives. Therefore, start after a sentinel if there is one and // skip over any align node and bit range nodes. - var i = if (kind == .sentinel or kind == .slice_sentinel) blk: { - assert(info.sentinel != 0); - break :blk tree.lastToken(info.sentinel) + 1; - } else blk: { - assert(info.sentinel == 0); - break :blk info.main_token; - }; + var i = if (info.sentinel != 0) tree.lastToken(info.sentinel) + 1 else info.main_token; const end = tree.firstToken(info.child_type); while (i < end) : (i += 1) { switch (token_tags[i]) { @@ -2115,7 +2104,7 @@ pub const full = struct { .comptime_noalias = comptime_noalias, .name_token = name_token, .anytype_ellipsis3 = it.tok_i - 1, - .type_expr = param_type, + .type_expr = 0, }; } it.tok_flag = false; @@ -2166,21 +2155,12 @@ pub const full = struct { }; pub const PtrType = struct { - kind: Kind, + size: std.builtin.TypeInfo.Pointer.Size, allowzero_token: ?TokenIndex, const_token: ?TokenIndex, volatile_token: ?TokenIndex, ast: Ast, - pub const Kind = enum { - one, - many, - sentinel, - c, - slice, - slice_sentinel, - }; - pub const Ast = struct { main_token: TokenIndex, align_node: Node.Index, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index f7608fe61a..c169a48b01 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -677,8 +677,8 @@ fn renderPtrType( ptr_type: ast.full.PtrType, space: Space, ) Error!void { - switch (ptr_type.kind) { - .one => { + switch (ptr_type.size) { + .One => { // Since ** tokens exist and the same token is shared by two // nested pointer types, we check to see if we are the parent // in such a relationship. If so, skip rendering anything for @@ -691,33 +691,35 @@ fn renderPtrType( } try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk }, - .many => { - try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket - }, - .sentinel => { - try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon - try renderExpression(ais, tree, ptr_type.ast.sentinel, .none); - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket + .Many => { + if (ptr_type.ast.sentinel == 0) { + try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket + try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk + try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket + } else { + try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket + try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk + try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon + try renderExpression(ais, tree, ptr_type.ast.sentinel, .none); + try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket + } }, - .c => { + .C => { try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket }, - .slice => { - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket - }, - .slice_sentinel => { - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon - try renderExpression(ais, tree, ptr_type.ast.sentinel, .none); - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket + .Slice => { + if (ptr_type.ast.sentinel == 0) { + try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket + try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket + } else { + try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket + try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon + try renderExpression(ais, tree, ptr_type.ast.sentinel, .none); + try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket + } }, } diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 9776edfef3..deb1cbfa76 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -115,727 +115,730 @@ allows_lvalue: bool = false, /// of parameters. param_count: ?u8, -pub const list = std.ComptimeStringMap(@This(), .{ - .{ - "@addWithOverflow", +pub const list = list: { + @setEvalBranchQuota(3000); + break :list std.ComptimeStringMap(@This(), .{ .{ - .tag = .add_with_overflow, - .param_count = 4, + "@addWithOverflow", + .{ + .tag = .add_with_overflow, + .param_count = 4, + }, }, - }, - .{ - "@alignCast", .{ - .tag = align_cast, - .param_count = 1, + "@alignCast", + .{ + .tag = .align_cast, + .param_count = 1, + }, }, - }, - .{ - "@alignOf", .{ - .tag = .align_of, - .param_count = 1, + "@alignOf", + .{ + .tag = .align_of, + .param_count = 1, + }, }, - }, - .{ - "@as", .{ - .tag = .as, - .needs_mem_loc = true, - .param_count = 2, + "@as", + .{ + .tag = .as, + .needs_mem_loc = true, + .param_count = 2, + }, }, - }, - .{ - "@asyncCall", .{ - .tag = .async_call, - .param_count = null, + "@asyncCall", + .{ + .tag = .async_call, + .param_count = null, + }, }, - }, - .{ - "@atomicLoad", .{ - .tag = .atomic_load, - .param_count = 3, + "@atomicLoad", + .{ + .tag = .atomic_load, + .param_count = 3, + }, }, - }, - .{ - "@atomicRmw", .{ - .tag = .atomic_rmw, - .param_count = 5, + "@atomicRmw", + .{ + .tag = .atomic_rmw, + .param_count = 5, + }, }, - }, - .{ - "@atomicStore", .{ - .tag = .atomic_store, - .param_count = 4, + "@atomicStore", + .{ + .tag = .atomic_store, + .param_count = 4, + }, }, - }, - .{ - "@bitCast", .{ - .tag = .bit_cast, - .needs_mem_loc = true, - .param_count = 2, + "@bitCast", + .{ + .tag = .bit_cast, + .needs_mem_loc = true, + .param_count = 2, + }, }, - }, - .{ - "@bitOffsetOf", .{ - .tag = .bit_offset_of, - .param_count = 2, + "@bitOffsetOf", + .{ + .tag = .bit_offset_of, + .param_count = 2, + }, }, - }, - .{ - "@boolToInt", .{ - .tag = .bool_to_int, - .param_count = 1, + "@boolToInt", + .{ + .tag = .bool_to_int, + .param_count = 1, + }, }, - }, - .{ - "@bitSizeOf", .{ - .tag = .bit_size_of, - .param_count = 1, + "@bitSizeOf", + .{ + .tag = .bit_size_of, + .param_count = 1, + }, }, - }, - .{ - "@breakpoint", .{ - .tag = .breakpoint, - .param_count = 0, + "@breakpoint", + .{ + .tag = .breakpoint, + .param_count = 0, + }, }, - }, - .{ - "@mulAdd", .{ - .tag = .mul_add, - .param_count = 4, + "@mulAdd", + .{ + .tag = .mul_add, + .param_count = 4, + }, }, - }, - .{ - "@byteSwap", .{ - .tag = .byte_swap, - .param_count = 2, + "@byteSwap", + .{ + .tag = .byte_swap, + .param_count = 2, + }, }, - }, - .{ - "@bitReverse", .{ - .tag = .bit_reverse, - .param_count = 2, + "@bitReverse", + .{ + .tag = .bit_reverse, + .param_count = 2, + }, }, - }, - .{ - "@byteOffsetOf", .{ - .tag = .byte_offset_of, - .param_count = 2, + "@byteOffsetOf", + .{ + .tag = .byte_offset_of, + .param_count = 2, + }, }, - }, - .{ - "@call", .{ - .tag = .call, - .needs_mem_loc = true, - .param_count = 3, + "@call", + .{ + .tag = .call, + .needs_mem_loc = true, + .param_count = 3, + }, }, - }, - .{ - "@cDefine", .{ - .tag = .c_define, - .param_count = 2, + "@cDefine", + .{ + .tag = .c_define, + .param_count = 2, + }, }, - }, - .{ - "@cImport", .{ - .tag = .c_import, - .param_count = 1, + "@cImport", + .{ + .tag = .c_import, + .param_count = 1, + }, }, - }, - .{ - "@cInclude", .{ - .tag = .c_include, - .param_count = 1, + "@cInclude", + .{ + .tag = .c_include, + .param_count = 1, + }, }, - }, - .{ - "@clz", .{ - .tag = .clz, - .param_count = 2, + "@clz", + .{ + .tag = .clz, + .param_count = 2, + }, }, - }, - .{ - "@cmpxchgStrong", .{ - .tag = .cmpxchg_strong, - .param_count = 6, + "@cmpxchgStrong", + .{ + .tag = .cmpxchg_strong, + .param_count = 6, + }, }, - }, - .{ - "@cmpxchgWeak", .{ - .tag = .cmpxchg_weak, - .param_count = 6, + "@cmpxchgWeak", + .{ + .tag = .cmpxchg_weak, + .param_count = 6, + }, }, - }, - .{ - "@compileError", .{ - .tag = .compile_error, - .param_count = 1, + "@compileError", + .{ + .tag = .compile_error, + .param_count = 1, + }, }, - }, - .{ - "@compileLog", .{ - .tag = .compile_log, - .param_count = null, + "@compileLog", + .{ + .tag = .compile_log, + .param_count = null, + }, }, - }, - .{ - "@ctz", .{ - .tag = .ctz, - .param_count = 2, + "@ctz", + .{ + .tag = .ctz, + .param_count = 2, + }, }, - }, - .{ - "@cUndef", .{ - .tag = .c_undef, - .param_count = 1, + "@cUndef", + .{ + .tag = .c_undef, + .param_count = 1, + }, }, - }, - .{ - "@divExact", .{ - .tag = .div_exact, - .param_count = 2, + "@divExact", + .{ + .tag = .div_exact, + .param_count = 2, + }, }, - }, - .{ - "@divFloor", .{ - .tag = .div_floor, - .param_count = 2, + "@divFloor", + .{ + .tag = .div_floor, + .param_count = 2, + }, }, - }, - .{ - "@divTrunc", .{ - .tag = .div_trunc, - .param_count = 2, + "@divTrunc", + .{ + .tag = .div_trunc, + .param_count = 2, + }, }, - }, - .{ - "@embedFile", .{ - .tag = .embed_file, - .param_count = 1, + "@embedFile", + .{ + .tag = .embed_file, + .param_count = 1, + }, }, - }, - .{ - "@enumToInt", .{ - .tag = .enum_to_int, - .param_count = 1, + "@enumToInt", + .{ + .tag = .enum_to_int, + .param_count = 1, + }, }, - }, - .{ - "@errorName", .{ - .tag = .error_name, - .param_count = 1, + "@errorName", + .{ + .tag = .error_name, + .param_count = 1, + }, }, - }, - .{ - "@errorReturnTrace", .{ - .tag = .error_return_trace, - .param_count = 0, + "@errorReturnTrace", + .{ + .tag = .error_return_trace, + .param_count = 0, + }, }, - }, - .{ - "@errorToInt", .{ - .tag = .error_to_int, - .param_count = 1, + "@errorToInt", + .{ + .tag = .error_to_int, + .param_count = 1, + }, }, - }, - .{ - "@errSetCast", .{ - .tag = .err_set_cast, - .param_count = 2, + "@errSetCast", + .{ + .tag = .err_set_cast, + .param_count = 2, + }, }, - }, - .{ - "@export", .{ - .tag = .@"export", - .param_count = 2, + "@export", + .{ + .tag = .@"export", + .param_count = 2, + }, }, - }, - .{ - "@fence", .{ - .tag = .fence, - .param_count = 0, + "@fence", + .{ + .tag = .fence, + .param_count = 0, + }, }, - }, - .{ - "@field", .{ - .tag = .field, - .needs_mem_loc = true, - .param_count = 2, - .allows_lvalue = true, + "@field", + .{ + .tag = .field, + .needs_mem_loc = true, + .param_count = 2, + .allows_lvalue = true, + }, }, - }, - .{ - "@fieldParentPtr", .{ - .tag = .field_parent_ptr, - .param_count = 3, + "@fieldParentPtr", + .{ + .tag = .field_parent_ptr, + .param_count = 3, + }, }, - }, - .{ - "@floatCast", .{ - .tag = .float_cast, - .param_count = 1, + "@floatCast", + .{ + .tag = .float_cast, + .param_count = 1, + }, }, - }, - .{ - "@floatToInt", .{ - .tag = .float_to_int, - .param_count = 1, + "@floatToInt", + .{ + .tag = .float_to_int, + .param_count = 1, + }, }, - }, - .{ - "@frame", .{ - .tag = .frame, - .param_count = 0, + "@frame", + .{ + .tag = .frame, + .param_count = 0, + }, }, - }, - .{ - "@Frame", .{ - .tag = .Frame, - .param_count = 1, + "@Frame", + .{ + .tag = .Frame, + .param_count = 1, + }, }, - }, - .{ - "@frameAddress", .{ - .tag = .frame_address, - .param_count = 0, + "@frameAddress", + .{ + .tag = .frame_address, + .param_count = 0, + }, }, - }, - .{ - "@frameSize", .{ - .tag = .frame_size, - .param_count = 1, + "@frameSize", + .{ + .tag = .frame_size, + .param_count = 1, + }, }, - }, - .{ - "@hasDecl", .{ - .tag = .has_decl, - .param_count = 2, + "@hasDecl", + .{ + .tag = .has_decl, + .param_count = 2, + }, }, - }, - .{ - "@hasField", .{ - .tag = .has_field, - .param_count = 2, + "@hasField", + .{ + .tag = .has_field, + .param_count = 2, + }, }, - }, - .{ - "@import", .{ - .tag = .import, - .param_count = 1, + "@import", + .{ + .tag = .import, + .param_count = 1, + }, }, - }, - .{ - "@intCast", .{ - .tag = .int_cast, - .param_count = 1, + "@intCast", + .{ + .tag = .int_cast, + .param_count = 1, + }, }, - }, - .{ - "@intToEnum", .{ - .tag = .int_to_enum, - .param_count = 1, + "@intToEnum", + .{ + .tag = .int_to_enum, + .param_count = 1, + }, }, - }, - .{ - "@intToError", .{ - .tag = .int_to_error, - .param_count = 1, + "@intToError", + .{ + .tag = .int_to_error, + .param_count = 1, + }, }, - }, - .{ - "@intToFloat", .{ - .tag = .int_to_float, - .param_count = 1, + "@intToFloat", + .{ + .tag = .int_to_float, + .param_count = 1, + }, }, - }, - .{ - "@intToPtr", .{ - .tag = .int_to_ptr, - .param_count = 2, + "@intToPtr", + .{ + .tag = .int_to_ptr, + .param_count = 2, + }, }, - }, - .{ - "@memcpy", .{ - .tag = .memcpy, - .param_count = 3, + "@memcpy", + .{ + .tag = .memcpy, + .param_count = 3, + }, }, - }, - .{ - "@memset", .{ - .tag = .memset, - .param_count = 3, + "@memset", + .{ + .tag = .memset, + .param_count = 3, + }, }, - }, - .{ - "@wasmMemorySize", .{ - .tag = .wasm_memory_size, - .param_count = 1, + "@wasmMemorySize", + .{ + .tag = .wasm_memory_size, + .param_count = 1, + }, }, - }, - .{ - "@wasmMemoryGrow", .{ - .tag = .wasm_memory_grow, - .param_count = 2, + "@wasmMemoryGrow", + .{ + .tag = .wasm_memory_grow, + .param_count = 2, + }, }, - }, - .{ - "@mod", .{ - .tag = .mod, - .param_count = 2, + "@mod", + .{ + .tag = .mod, + .param_count = 2, + }, }, - }, - .{ - "@mulWithOverflow", .{ - .tag = .mul_with_overflow, - .param_count = 4, + "@mulWithOverflow", + .{ + .tag = .mul_with_overflow, + .param_count = 4, + }, }, - }, - .{ - "@panic", .{ - .tag = .panic, - .param_count = 1, + "@panic", + .{ + .tag = .panic, + .param_count = 1, + }, }, - }, - .{ - "@popCount", .{ - .tag = .pop_count, - .param_count = 2, + "@popCount", + .{ + .tag = .pop_count, + .param_count = 2, + }, }, - }, - .{ - "@ptrCast", .{ - .tag = .ptr_cast, - .param_count = 2, + "@ptrCast", + .{ + .tag = .ptr_cast, + .param_count = 2, + }, }, - }, - .{ - "@ptrToInt", .{ - .tag = .ptr_to_int, - .param_count = 1, + "@ptrToInt", + .{ + .tag = .ptr_to_int, + .param_count = 1, + }, }, - }, - .{ - "@rem", .{ - .tag = .rem, - .param_count = 2, + "@rem", + .{ + .tag = .rem, + .param_count = 2, + }, }, - }, - .{ - "@returnAddress", .{ - .tag = .return_address, - .param_count = 0, + "@returnAddress", + .{ + .tag = .return_address, + .param_count = 0, + }, }, - }, - .{ - "@setAlignStack", .{ - .tag = .set_align_stack, - .param_count = 1, + "@setAlignStack", + .{ + .tag = .set_align_stack, + .param_count = 1, + }, }, - }, - .{ - "@setCold", .{ - .tag = .set_cold, - .param_count = 1, + "@setCold", + .{ + .tag = .set_cold, + .param_count = 1, + }, }, - }, - .{ - "@setEvalBranchQuota", .{ - .tag = .set_eval_branch_quota, - .param_count = 1, + "@setEvalBranchQuota", + .{ + .tag = .set_eval_branch_quota, + .param_count = 1, + }, }, - }, - .{ - "@setFloatMode", .{ - .tag = .set_float_mode, - .param_count = 1, + "@setFloatMode", + .{ + .tag = .set_float_mode, + .param_count = 1, + }, }, - }, - .{ - "@setRuntimeSafety", .{ - .tag = .set_runtime_safety, - .param_count = 1, + "@setRuntimeSafety", + .{ + .tag = .set_runtime_safety, + .param_count = 1, + }, }, - }, - .{ - "@shlExact", .{ - .tag = .shl_exact, - .param_count = 2, + "@shlExact", + .{ + .tag = .shl_exact, + .param_count = 2, + }, }, - }, - .{ - "@shlWithOverflow", .{ - .tag = .shl_with_overflow, - .param_count = 4, + "@shlWithOverflow", + .{ + .tag = .shl_with_overflow, + .param_count = 4, + }, }, - }, - .{ - "@shrExact", .{ - .tag = .shr_exact, - .param_count = 2, + "@shrExact", + .{ + .tag = .shr_exact, + .param_count = 2, + }, }, - }, - .{ - "@shuffle", .{ - .tag = .shuffle, - .param_count = 4, + "@shuffle", + .{ + .tag = .shuffle, + .param_count = 4, + }, }, - }, - .{ - "@sizeOf", .{ - .tag = .size_of, - .param_count = 1, + "@sizeOf", + .{ + .tag = .size_of, + .param_count = 1, + }, }, - }, - .{ - "@splat", .{ - .tag = .splat, - .needs_mem_loc = true, - .param_count = 2, + "@splat", + .{ + .tag = .splat, + .needs_mem_loc = true, + .param_count = 2, + }, }, - }, - .{ - "@reduce", .{ - .tag = .reduce, - .param_count = 2, + "@reduce", + .{ + .tag = .reduce, + .param_count = 2, + }, }, - }, - .{ - "@src", .{ - .tag = .src, - .needs_mem_loc = true, - .param_count = 0, + "@src", + .{ + .tag = .src, + .needs_mem_loc = true, + .param_count = 0, + }, }, - }, - .{ - "@sqrt", .{ - .tag = .sqrt, - .param_count = 1, + "@sqrt", + .{ + .tag = .sqrt, + .param_count = 1, + }, }, - }, - .{ - "@sin", .{ - .tag = .sin, - .param_count = 1, + "@sin", + .{ + .tag = .sin, + .param_count = 1, + }, }, - }, - .{ - "@cos", .{ - .tag = .cos, - .param_count = 1, + "@cos", + .{ + .tag = .cos, + .param_count = 1, + }, }, - }, - .{ - "@exp", .{ - .tag = .exp, - .param_count = 1, + "@exp", + .{ + .tag = .exp, + .param_count = 1, + }, }, - }, - .{ - "@exp2", .{ - .tag = .exp2, - .param_count = 1, + "@exp2", + .{ + .tag = .exp2, + .param_count = 1, + }, }, - }, - .{ - "@log", .{ - .tag = .log, - .param_count = 1, + "@log", + .{ + .tag = .log, + .param_count = 1, + }, }, - }, - .{ - "@log2", .{ - .tag = .log2, - .param_count = 1, + "@log2", + .{ + .tag = .log2, + .param_count = 1, + }, }, - }, - .{ - "@log10", .{ - .tag = .log10, - .param_count = 1, + "@log10", + .{ + .tag = .log10, + .param_count = 1, + }, }, - }, - .{ - "@fabs", .{ - .tag = .fabs, - .param_count = 1, + "@fabs", + .{ + .tag = .fabs, + .param_count = 1, + }, }, - }, - .{ - "@floor", .{ - .tag = .floor, - .param_count = 1, + "@floor", + .{ + .tag = .floor, + .param_count = 1, + }, }, - }, - .{ - "@ceil", .{ - .tag = .ceil, - .param_count = 1, + "@ceil", + .{ + .tag = .ceil, + .param_count = 1, + }, }, - }, - .{ - "@trunc", .{ - .tag = .trunc, - .param_count = 1, + "@trunc", + .{ + .tag = .trunc, + .param_count = 1, + }, }, - }, - .{ - "@round", .{ - .tag = .round, - .param_count = 1, + "@round", + .{ + .tag = .round, + .param_count = 1, + }, }, - }, - .{ - "@subWithOverflow", .{ - .tag = .sub_with_overflow, - .param_count = 4, + "@subWithOverflow", + .{ + .tag = .sub_with_overflow, + .param_count = 4, + }, }, - }, - .{ - "@tagName", .{ - .tag = .tag_name, - .param_count = 1, + "@tagName", + .{ + .tag = .tag_name, + .param_count = 1, + }, }, - }, - .{ - "@This", .{ - .tag = .This, - .param_count = 0, + "@This", + .{ + .tag = .This, + .param_count = 0, + }, }, - }, - .{ - "@truncate", .{ - .tag = .truncate, - .param_count = 2, + "@truncate", + .{ + .tag = .truncate, + .param_count = 2, + }, }, - }, - .{ - "@Type", .{ - .tag = .Type, - .param_count = 1, + "@Type", + .{ + .tag = .Type, + .param_count = 1, + }, }, - }, - .{ - "@typeInfo", .{ - .tag = .type_info, - .param_count = 1, + "@typeInfo", + .{ + .tag = .type_info, + .param_count = 1, + }, }, - }, - .{ - "@typeName", .{ - .tag = .type_name, - .param_count = 1, + "@typeName", + .{ + .tag = .type_name, + .param_count = 1, + }, }, - }, - .{ - "@TypeOf", .{ - .tag = .TypeOf, - .param_count = null, + "@TypeOf", + .{ + .tag = .TypeOf, + .param_count = null, + }, }, - }, - .{ - "@unionInit", .{ - .tag = .union_init, - .needs_mem_loc = true, - .param_count = 3, + "@unionInit", + .{ + .tag = .union_init, + .needs_mem_loc = true, + .param_count = 3, + }, }, - }, -}); + }); +}; diff --git a/src/Module.zig b/src/Module.zig index d2530d7df3..35819c5d44 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -430,12 +430,12 @@ pub const Scope = struct { /// Asserts the scope is a child of a File and has an AST tree and returns the tree. pub fn tree(self: *Scope) *const ast.Tree { switch (self.tag) { - .file => return self.cast(File).?.tree, - .block => return self.cast(Block).?.src_decl.container.file_scope.tree, - .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.tree, - .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, - .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, - .container => return self.cast(Container).?.file_scope.tree, + .file => return &self.cast(File).?.tree, + .block => return &self.cast(Block).?.src_decl.container.file_scope.tree, + .gen_zir => return &self.cast(GenZIR).?.decl.container.file_scope.tree, + .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, + .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, + .container => return &self.cast(Container).?.file_scope.tree, } } @@ -1612,6 +1612,7 @@ fn astgenAndSemaVarDecl( .decl = decl, .arena = &type_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer type_scope.instructions.deinit(mod.gpa); @@ -1630,7 +1631,7 @@ fn astgenAndSemaVarDecl( } else { return mod.failTok( &block_scope.base, - tree.firstToken(var_decl), + var_decl.ast.mut_token, "unable to infer variable type", .{}, ); @@ -1639,7 +1640,7 @@ fn astgenAndSemaVarDecl( if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { return mod.failTok( &block_scope.base, - tree.firstToken(var_decl), + var_decl.ast.mut_token, "variable of type '{}' must be const", .{var_info.ty}, ); diff --git a/src/astgen.zig b/src/astgen.zig index a018d58d2f..3e5d63796f 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -59,6 +59,8 @@ pub const ResultLoc = union(enum) { pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!*zir.Inst { const tree = scope.tree(); + const token_starts = tree.tokens.items(.start); + const type_src = token_starts[tree.firstToken(type_node)]; const type_type = try addZIRInstConst(mod, scope, type_src, .{ .ty = Type.initTag(.type), @@ -76,13 +78,17 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.I .root => unreachable, .@"usingnamespace" => unreachable, .test_decl => unreachable, - .doc_comment => unreachable, - .var_decl => unreachable, + .global_var_decl => unreachable, + .local_var_decl => unreachable, + .simple_var_decl => unreachable, + .aligned_var_decl => unreachable, .switch_case => unreachable, - .switch_else => unreachable, + .switch_case_one => unreachable, .container_field_init => unreachable, .container_field_align => unreachable, .container_field => unreachable, + .asm_output => unreachable, + .asm_input => unreachable, .assign, .assign_bit_and, @@ -122,58 +128,107 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.I .bool_and, .bool_or, .@"asm", + .asm_simple, .string_literal, .integer_literal, .call, - .@"unreachable", + .call_comma, + .async_call, + .async_call_comma, + .call_one, + .call_one_comma, + .async_call_one, + .async_call_one_comma, + .unreachable_literal, .@"return", .@"if", + .if_simple, .@"while", + .while_simple, + .while_cont, .bool_not, .address_of, .float_literal, .undefined_literal, - .bool_literal, + .true_literal, + .false_literal, .null_literal, .optional_type, .block, - .labeled_block, + .block_semicolon, + .block_two, + .block_two_semicolon, .@"break", - .PtrType, + .ptr_type_aligned, + .ptr_type_sentinel, + .ptr_type, + .ptr_type_bit_range, .array_type, .array_type_sentinel, .enum_literal, - .MultilineStringLiteral, + .multiline_string_literal, .char_literal, .@"defer", + .@"errdefer", .@"catch", .error_union, .merge_error_sets, - .range, + .switch_range, .@"await", .bit_not, .negation, .negation_wrap, .@"resume", .@"try", - .slice_type, .slice, - .ArrayInitializer, - .ArrayInitializerDot, - .StructInitializer, - .StructInitializerDot, + .slice_open, + .slice_sentinel, + .array_init_one, + .array_init_one_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init, + .array_init_comma, + .struct_init_one, + .struct_init_one_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init, + .struct_init_comma, .@"switch", + .switch_comma, .@"for", + .for_simple, .@"suspend", .@"continue", .@"anytype", - .error_type, - .FnProto, + .fn_proto_simple, + .fn_proto_multi, + .fn_proto_one, + .fn_proto, + .fn_decl, .anyframe_type, + .anyframe_literal, .error_set_decl, - .ContainerDecl, + .container_decl, + .container_decl_comma, + .container_decl_two, + .container_decl_two_comma, + .container_decl_arg, + .container_decl_arg_comma, + .tagged_union, + .tagged_union_comma, + .tagged_union_two, + .tagged_union_two_comma, + .tagged_union_enum_tag, + .tagged_union_enum_tag_comma, .@"comptime", .@"nosuspend", + .error_value, => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), .builtin_call, @@ -192,10 +247,10 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.I } }, - // can be assigned to + // These can be assigned to. .unwrap_optional, .deref, - .period, + .field_access, .array_access, .identifier, .grouped_expression, @@ -210,22 +265,33 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.I /// result instruction can be used to inspect whether it is isNoReturn() but that is it, /// it must otherwise not be used. pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { - switch (node.tag) { + const tree = scope.tree(); + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const token_starts = tree.tokens.items(.start); + + switch (node_tags[node]) { .root => unreachable, // Top-level declaration. .@"usingnamespace" => unreachable, // Top-level declaration. .test_decl => unreachable, // Top-level declaration. - .doc_comment => unreachable, // Top-level declaration. - .var_decl => unreachable, // Handled in `blockExpr`. + .container_field_init => unreachable, // Top-level declaration. + .container_field_align => unreachable, // Top-level declaration. + .container_field => unreachable, // Top-level declaration. + .fn_decl => unreachable, // Top-level declaration. + + .global_var_decl => unreachable, // Handled in `blockExpr`. + .local_var_decl => unreachable, // Handled in `blockExpr`. + .simple_var_decl => unreachable, // Handled in `blockExpr`. + .aligned_var_decl => unreachable, // Handled in `blockExpr`. + .switch_case => unreachable, // Handled in `switchExpr`. - .switch_else => unreachable, // Handled in `switchExpr`. - .range => unreachable, // Handled in `switchExpr`. - .Else => unreachable, // Handled explicitly the control flow expression functions. - .Payload => unreachable, // Handled explicitly. - .PointerPayload => unreachable, // Handled explicitly. - .PointerIndexPayload => unreachable, // Handled explicitly. - .ErrorTag => unreachable, // Handled explicitly. - .FieldInitializer => unreachable, // Handled explicitly. - .ContainerField => unreachable, // Handled explicitly. + .switch_case_one => unreachable, // Handled in `switchExpr`. + .switch_range => unreachable, // Handled in `switchExpr`. + + .asm_output => unreachable, // Handled in `asmExpr`. + .asm_input => unreachable, // Handled in `asmExpr`. .assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node)), .assign_bit_and => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_and)), @@ -276,30 +342,28 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .identifier => return identifier(mod, scope, rl, node), - .asm_simple => return assembly(mod, scope, rl, tree.asmSimple(node)), - .@"asm" => return assembly(mod, scope, rl, tree.asmFull(node)), + .asm_simple => return asmExpr(mod, scope, rl, tree.asmSimple(node)), + .@"asm" => return asmExpr(mod, scope, rl, tree.asmFull(node)), .string_literal => return stringLiteral(mod, scope, rl, node), .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node), .integer_literal => return integerLiteral(mod, scope, rl, node), - .builtin_call => return builtinCall(mod, scope, rl, node), - .builtin_call_two, .builtin_call_two_comma => { - if (datas[node].lhs == 0) { + if (node_datas[node].lhs == 0) { const params = [_]ast.Node.Index{}; return builtinCall(mod, scope, rl, node, ¶ms); - } else if (datas[node].rhs == 0) { - const params = [_]ast.Node.Index{datas[node].lhs}; + } else if (node_datas[node].rhs == 0) { + const params = [_]ast.Node.Index{node_datas[node].lhs}; return builtinCall(mod, scope, rl, node, ¶ms); } else { - const params = [_]ast.Node.Index{ datas[node].lhs, datas[node].rhs }; + const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; return builtinCall(mod, scope, rl, node, ¶ms); } }, .builtin_call, .builtin_call_comma => { - const params = tree.extra_data[datas[node].lhs..datas[node].rhs]; + const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; return builtinCall(mod, scope, rl, node, params); }, @@ -311,20 +375,20 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In return callExpr(mod, scope, rl, tree.callFull(node)); }, - .@"unreachable" => { + .unreachable_literal => { const main_token = main_tokens[node]; const src = token_starts[main_token]; return addZIRNoOp(mod, scope, src, .unreachable_safe); }, .@"return" => return ret(mod, scope, node), - .period => return field(mod, scope, rl, node), + .field_access => return field(mod, scope, rl, node), .float_literal => return floatLiteral(mod, scope, rl, node), .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)), - .@"if" => return ifExpr(mode, scope, rl, tree.ifFull(node)), + .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)), .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)), - .while_cont => return whileExpr(mod, scope, tree.whileCont(node)), + .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)), .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)), .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)), @@ -389,7 +453,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In return rvalue(mod, scope, rl, result); }, .unwrap_optional => { - const operand = try expr(mod, scope, rl, node.lhs); + const operand = try expr(mod, scope, rl, node_datas[node].lhs); const op: zir.Inst.Tag = switch (rl) { .ref => .optional_payload_safe_ptr, else => .optional_payload_safe, @@ -449,7 +513,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In }, .@"catch" => { const catch_token = main_tokens[node]; - const payload_token: ?TokenIndex = if (token_tags[catch_token + 1] == .pipe) + const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) catch_token + 2 else null; @@ -506,6 +570,34 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In null, ), }, + + .ptr_type_aligned => return ptrType(mod, scope, rl, tree.ptrTypeAligned(node)), + .ptr_type_sentinel => return ptrType(mod, scope, rl, tree.ptrTypeSentinel(node)), + .ptr_type => return ptrType(mod, scope, rl, tree.ptrType(node)), + .ptr_type_bit_range => return ptrType(mod, scope, rl, tree.ptrTypeBitRange(node)), + + .container_decl, + .container_decl_comma, + => return containerDecl(mod, scope, rl, tree.containerDecl(node)), + .container_decl_two, .container_decl_two_comma => { + var buffer: [2]ast.Node.Index = undefined; + return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node)); + }, + .container_decl_arg, + .container_decl_arg_comma, + => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)), + + .tagged_union, + .tagged_union_comma, + => return containerDecl(mod, scope, rl, tree.taggedUnion(node)), + .tagged_union_two, .tagged_union_two_comma => { + var buffer: [2]ast.Node.Index = undefined; + return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node)); + }, + .tagged_union_enum_tag, + .tagged_union_enum_tag_comma, + => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)), + .@"break" => return breakExpr(mod, scope, rl, node), .@"continue" => return continueExpr(mod, scope, rl, node), .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs), @@ -518,12 +610,41 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), + .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), + + .array_init_one, + .array_init_one_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init, + .array_init_comma, + => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}), + + .struct_init_one, + .struct_init_one_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init, + .struct_init_comma, + => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), + .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), + .fn_proto_simple, + .fn_proto_multi, + .fn_proto_one, + .fn_proto, + => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), + .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), + .error_value => return mod.failNode(scope, node, "TODO implement astgen.expr for .error_value", .{}), } } @@ -572,6 +693,8 @@ fn breakExpr( const tree = parent_scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const src = token_starts[main_tokens[node]]; const break_label = node_datas[node].lhs; const rhs = node_datas[node].rhs; @@ -646,6 +769,8 @@ fn continueExpr( const tree = parent_scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const src = token_starts[main_tokens[node]]; const break_label = node_datas[node].lhs; @@ -702,7 +827,7 @@ pub fn blockExpr( const main_tokens = tree.nodes.items(.main_token); const token_tags = tree.tokens.items(.tag); - const lbrace = main_tokens[node]; + const lbrace = main_tokens[block_node]; if (token_tags[lbrace - 1] == .colon) { return labeledBlockExpr(mod, scope, rl, block_node, statements, .block); } @@ -721,8 +846,9 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn if (gen_zir.label) |prev_label| { if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { const tree = parent_scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const label_src = token_starts[label]; const prev_label_src = token_starts[prev_label.token]; @@ -770,9 +896,9 @@ fn labeledBlockExpr( assert(zir_tag == .block or zir_tag == .block_comptime); const tree = parent_scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); const lbrace = main_tokens[block_node]; const label_token = lbrace - 1; @@ -813,10 +939,10 @@ fn labeledBlockExpr( defer block_scope.labeled_breaks.deinit(mod.gpa); defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); - try blockExprStmts(mod, &block_scope.base, block_node, block_node.statements()); + try blockExprStmts(mod, &block_scope.base, block_node, statements); if (!block_scope.label.?.used) { - return mod.fail(parent_scope, token_starts[block_node.label], "unused block label", .{}); + return mod.failTok(parent_scope, label_token, "unused block label", .{}); } try gen_zir.instructions.append(mod.gpa, &block_inst.base); @@ -860,21 +986,23 @@ fn blockExprStmts( statements: []const ast.Node.Index, ) !void { const tree = parent_scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const node_tags = tree.nodes.items(.tag); var block_arena = std.heap.ArenaAllocator.init(mod.gpa); defer block_arena.deinit(); var scope = parent_scope; for (statements) |statement| { - const src = token_starts[statement.firstToken()]; + const src = token_starts[tree.firstToken(statement)]; _ = try addZIRNoOp(mod, scope, src, .dbg_stmt); - switch (statement.tag) { - .var_decl => { - const var_decl_node = statement.castTag(.var_decl).?; - scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator); - }, + switch (node_tags[statement]) { + .global_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.globalVarDecl(statement)), + .local_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.localVarDecl(statement)), + .simple_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.simpleVarDecl(statement)), + .aligned_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.alignedVarDecl(statement)), + .assign => try assign(mod, scope, statement), .assign_bit_and => try assignOp(mod, scope, statement, .bit_and), .assign_bit_or => try assignOp(mod, scope, statement, .bit_or), @@ -903,20 +1031,23 @@ fn blockExprStmts( fn varDecl( mod: *Module, scope: *Scope, - node: *ast.Node.var_decl, block_arena: *Allocator, + var_decl: ast.full.VarDecl, ) InnerError!*Scope { - if (node.getComptimeToken()) |comptime_token| { + if (var_decl.comptime_token) |comptime_token| { return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); } - if (node.getAlignNode()) |align_node| { - return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{}); + if (var_decl.ast.align_node != 0) { + return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{}); } const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); - const name_src = token_starts[node.name_token]; - const ident_name = try mod.identifierTokenString(scope, node.name_token); + const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); + + const name_token = var_decl.ast.mut_token + 1; + const name_src = token_starts[name_token]; + const ident_name = try mod.identifierTokenString(scope, name_token); // Local variables shadowing detection, including function parameters. { @@ -962,20 +1093,21 @@ fn varDecl( // TODO add note for other definition return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } - const init_node = node.getInitNode() orelse + if (var_decl.ast.init_node == 0) { return mod.fail(scope, name_src, "variables must be initialized", .{}); + } - switch (tree.token_ids[node.mut_token]) { + switch (token_tags[var_decl.ast.mut_token]) { .keyword_const => { // Depending on the type of AST the initialization expression is, we may need an lvalue // or an rvalue as a result location. If it is an rvalue, we can use the instruction as // the variable, no memory location needed. - if (!nodeMayNeedMemoryLocation(init_node, scope)) { - const result_loc: ResultLoc = if (node.getTypeNode()) |type_node| - .{ .ty = try typeExpr(mod, scope, type_node) } + if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) { + const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) + .{ .ty = try typeExpr(mod, scope, var_decl.ast.type_node) } else .none; - const init_inst = try expr(mod, scope, result_loc, init_node); + const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node); const sub_scope = try block_arena.create(Scope.LocalVal); sub_scope.* = .{ .parent = scope, @@ -999,8 +1131,8 @@ fn varDecl( var resolve_inferred_alloc: ?*zir.Inst = null; var opt_type_inst: ?*zir.Inst = null; - if (node.getTypeNode()) |type_node| { - const type_inst = try typeExpr(mod, &init_scope.base, type_node); + if (var_decl.ast.type_node != 0) { + const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node); opt_type_inst = type_inst; init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst); } else { @@ -1009,7 +1141,7 @@ fn varDecl( init_scope.rl_ptr = &alloc.base; } const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; - const init_inst = try expr(mod, &init_scope.base, init_result_loc, init_node); + const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node); const parent_zir = &scope.getGenZIR().instructions; if (init_scope.rvalue_rl_count == 1) { // Result location pointer not used. We don't need an alloc for this @@ -1069,8 +1201,11 @@ fn varDecl( }, .keyword_var => { var resolve_inferred_alloc: ?*zir.Inst = null; - const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: { - const type_inst = try typeExpr(mod, scope, type_node); + const var_data: struct { + result_loc: ResultLoc, + alloc: *zir.Inst, + } = if (var_decl.ast.type_node != 0) a: { + const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node); const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst); break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; } else a: { @@ -1078,7 +1213,7 @@ fn varDecl( resolve_inferred_alloc = &alloc.base; break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } }; }; - const init_inst = try expr(mod, scope, var_data.result_loc, init_node); + const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node); if (resolve_inferred_alloc) |inst| { _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst); } @@ -1099,13 +1234,15 @@ fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!vo const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const node_tags = tree.nodes.items(.tag); + const lhs = node_datas[infix_node].lhs; const rhs = node_datas[infix_node].rhs; if (node_tags[lhs] == .identifier) { // This intentionally does not support `@"_"` syntax. const ident_name = tree.tokenSlice(main_tokens[lhs]); if (mem.eql(u8, ident_name, "_")) { - _ = try expr(mod, scope, .discard, infix_node.rhs); + _ = try expr(mod, scope, .discard, rhs); return; } } @@ -1122,6 +1259,7 @@ fn assignOp( const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs); const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); @@ -1136,6 +1274,7 @@ fn boolNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.In const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const src = token_starts[main_tokens[node]]; const bool_type = try addZIRInstConst(mod, scope, src, .{ @@ -1150,6 +1289,7 @@ fn bitNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Ins const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const src = token_starts[main_tokens[node]]; const operand = try expr(mod, scope, .none, node_datas[node].lhs); @@ -1165,6 +1305,7 @@ fn negation( const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const src = token_starts[main_tokens[node]]; const lhs = try addZIRInstConst(mod, scope, src, .{ @@ -1175,53 +1316,61 @@ fn negation( return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); } -fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst { +fn ptrType( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + ptr_info: ast.full.PtrType, +) InnerError!*zir.Inst { + const tree = scope.tree(); + const token_starts = tree.tokens.items(.start); + + const src = token_starts[ptr_info.ast.main_token]; + const simple = ptr_info.allowzero_token == null and - ptr_info.align_info == null and + ptr_info.ast.align_node == 0 and ptr_info.volatile_token == null and - ptr_info.sentinel == null; + ptr_info.ast.sentinel == 0; if (simple) { - const child_type = try typeExpr(mod, scope, rhs); + const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type); const mutable = ptr_info.const_token == null; - // TODO stage1 type inference bug const T = zir.Inst.Tag; - return addZIRUnOp(mod, scope, src, switch (size) { + const result = try addZIRUnOp(mod, scope, src, switch (ptr_info.size) { .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type, .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type, .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type, .Slice => if (mutable) T.mut_slice_type else T.const_slice_type, }, child_type); + return rvalue(mod, scope, rl, result); } var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{}; - kw_args.size = size; + kw_args.size = ptr_info.size; kw_args.@"allowzero" = ptr_info.allowzero_token != null; - if (ptr_info.align_info) |some| { - kw_args.@"align" = try expr(mod, scope, .none, some.node); - if (some.bit_range) |bit_range| { - kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start); - kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end); + if (ptr_info.ast.align_node != 0) { + kw_args.@"align" = try expr(mod, scope, .none, ptr_info.ast.align_node); + if (ptr_info.ast.bit_range_start != 0) { + kw_args.align_bit_start = try expr(mod, scope, .none, ptr_info.ast.bit_range_start); + kw_args.align_bit_end = try expr(mod, scope, .none, ptr_info.ast.bit_range_end); } } kw_args.mutable = ptr_info.const_token == null; kw_args.@"volatile" = ptr_info.volatile_token != null; - if (ptr_info.sentinel) |some| { - kw_args.sentinel = try expr(mod, scope, .none, some); - } - - const child_type = try typeExpr(mod, scope, rhs); - if (kw_args.sentinel) |some| { - kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some); + const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type); + if (ptr_info.ast.sentinel != 0) { + kw_args.sentinel = try expr(mod, scope, .{ .ty = child_type }, ptr_info.ast.sentinel); } - - return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); + const result = try addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); + return rvalue(mod, scope, rl, result); } fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { const tree = scope.tree(); const main_tokens = tree.nodes.items(.main_token); const node_datas = tree.nodes.items(.data); + const token_starts = tree.tokens.items(.start); + const src = token_starts[main_tokens[node]]; const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), @@ -1246,6 +1395,9 @@ fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) ! fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { const tree = scope.tree(); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const node_datas = tree.nodes.items(.data); + const len_node = node_datas[node].lhs; const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); const src = token_starts[main_tokens[node]]; @@ -1274,6 +1426,8 @@ fn containerField( node: *ast.Node.ContainerField, ) InnerError!*zir.Inst { const tree = scope.tree(); + const token_starts = tree.tokens.items(.start); + const src = token_starts[tree.firstToken(node)]; const name = try mod.identifierTokenString(scope, node.name_token); @@ -1305,9 +1459,18 @@ fn containerField( }); } -fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ContainerDecl) InnerError!*zir.Inst { +fn containerDecl( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + container_decl: ast.full.ContainerDecl, +) InnerError!*zir.Inst { const tree = scope.tree(); - const src = token_starts[node.kind_token]; + const token_starts = tree.tokens.items(.start); + const node_tags = tree.nodes.items(.tag); + const token_tags = tree.tokens.items(.tag); + + const src = token_starts[container_decl.ast.main_token]; var gen_scope: Scope.GenZIR = .{ .parent = scope, @@ -1321,9 +1484,12 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con var fields = std.ArrayList(*zir.Inst).init(mod.gpa); defer fields.deinit(); - for (node.fieldsAndDecls()) |fd| { - if (fd.castTag(.ContainerField)) |f| { - try fields.append(try containerField(mod, &gen_scope.base, f)); + for (container_decl.ast.members) |member| { + switch (node_tags[member]) { + .container_field_init, .container_field_align, .container_field => { + try fields.append(try containerField(mod, &gen_scope.base, member)); + }, + else => continue, } } @@ -1332,19 +1498,22 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con const arena = &decl_arena.allocator; var layout: std.builtin.TypeInfo.ContainerLayout = .Auto; - if (node.layout_token) |some| switch (tree.token_ids[some]) { + if (container_decl.layout_token) |some| switch (token_tags[some]) { .keyword_extern => layout = .Extern, .keyword_packed => layout = .Packed, else => unreachable, }; - const container_type = switch (tree.token_ids[node.kind_token]) { + // TODO this implementation is incorrect. The types must be created in semantic + // analysis, not astgen, because the same ZIR is re-used for multiple inline function calls, + // comptime function calls, and generic function instantiations, and these + // must result in different instances of container types. + const container_type = switch (token_tags[container_decl.ast.main_token]) { .keyword_enum => blk: { - const tag_type: ?*zir.Inst = switch (node.init_arg_expr) { - .Type => |t| try typeExpr(mod, &gen_scope.base, t), - .None => null, - .Enum => unreachable, - }; + const tag_type: ?*zir.Inst = if (container_decl.ast.arg != 0) + try typeExpr(mod, &gen_scope.base, container_decl.ast.arg) + else + null; const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{ .fields = try arena.dupe(*zir.Inst, fields.items), }, .{ @@ -1367,7 +1536,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con break :blk Type.initPayload(&enum_type.base); }, .keyword_struct => blk: { - assert(node.init_arg_expr == .None); + assert(container_decl.ast.arg == 0); const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{ .fields = try arena.dupe(*zir.Inst, fields.items), }, .{ @@ -1389,21 +1558,16 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con break :blk Type.initPayload(&struct_type.base); }, .keyword_union => blk: { - const init_inst = switch (node.init_arg_expr) { - .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null, - .None => null, - .Type => |t| try typeExpr(mod, &gen_scope.base, t), - }; - const init_kind: zir.Inst.UnionType.InitKind = switch (node.init_arg_expr) { - .Enum => .enum_type, - .None => .none, - .Type => .tag_type, - }; + const init_inst: ?*zir.Inst = if (container_decl.ast.arg != 0) + try typeExpr(mod, &gen_scope.base, container_decl.ast.arg) + else + null; + const has_enum_token = container_decl.ast.enum_token != null; const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{ .fields = try arena.dupe(*zir.Inst, fields.items), }, .{ .layout = layout, - .init_kind = init_kind, + .has_enum_token = has_enum_token, .init_inst = init_inst, }); const union_type = try arena.create(Type.Payload.Union); @@ -1437,7 +1601,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con else => unreachable, }; const val = try Value.Tag.ty.create(arena, container_type); - const decl = try mod.createContainerDecl(scope, node.kind_token, &decl_arena, .{ + const decl = try mod.createContainerDecl(scope, container_decl.ast.main_token, &decl_arena, .{ .ty = Type.initTag(.type), .val = val, }); @@ -1459,6 +1623,7 @@ fn errorSetDecl( const tree = scope.tree(); const main_tokens = tree.nodes.items(.main_token); const token_tags = tree.tokens.items(.tag); + const token_starts = tree.tokens.items(.start); // Count how many fields there are. const error_token = main_tokens[node]; @@ -1500,15 +1665,17 @@ fn orelseCatchExpr( mod: *Module, scope: *Scope, rl: ResultLoc, - lhs: *ast.Node, + lhs: ast.Node.Index, op_token: ast.TokenIndex, cond_op: zir.Inst.Tag, unwrap_op: zir.Inst.Tag, unwrap_code_op: zir.Inst.Tag, - rhs: *ast.Node, - payload_node: ?*ast.Node, + rhs: ast.Node.Index, + payload_token: ?ast.TokenIndex, ) InnerError!*zir.Inst { const tree = scope.tree(); + const token_starts = tree.tokens.items(.start); + const src = token_starts[op_token]; var block_scope: Scope.GenZIR = .{ @@ -1547,12 +1714,11 @@ fn orelseCatchExpr( var err_val_scope: Scope.LocalVal = undefined; const then_sub_scope = blk: { - const payload = payload_node orelse break :blk &then_scope.base; - - const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken()); - if (mem.eql(u8, err_name, "_")) - break :blk &then_scope.base; - + const payload = payload_token orelse break :blk &then_scope.base; + if (mem.eql(u8, tree.tokenSlice(payload), "_")) { + return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{}); + } + const err_name = try mod.identifierTokenString(scope, payload); err_val_scope = .{ .parent = &then_scope.base, .gen_zir = &then_scope, @@ -1685,18 +1851,20 @@ pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) I const tree = scope.tree(); const token_starts = tree.tokens.items(.start); const main_tokens = tree.nodes.items(.main_token); + const node_datas = tree.nodes.items(.data); + const dot_token = main_tokens[node]; const src = token_starts[dot_token]; const field_ident = dot_token + 1; const field_name = try mod.identifierTokenString(scope, field_ident); if (rl == .ref) { return addZirInstTag(mod, scope, src, .field_ptr, .{ - .object = try expr(mod, scope, .ref, node.lhs), + .object = try expr(mod, scope, .ref, node_datas[node].lhs), .field_name = field_name, }); } else { return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{ - .object = try expr(mod, scope, .none, node.lhs), + .object = try expr(mod, scope, .none, node_datas[node].lhs), .field_name = field_name, })); } @@ -1711,6 +1879,8 @@ fn arrayAccess( const tree = scope.tree(); const main_tokens = tree.nodes.items(.main_token); const token_starts = tree.tokens.items(.start); + const node_datas = tree.nodes.items(.data); + const src = token_starts[main_tokens[node]]; const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), @@ -1737,6 +1907,7 @@ fn sliceExpr( ) InnerError!*zir.Inst { const tree = scope.tree(); const token_starts = tree.tokens.items(.start); + const src = token_starts[slice.ast.lbracket]; const usize_type = try addZIRInstConst(mod, scope, src, .{ @@ -1786,6 +1957,7 @@ fn simpleBinOp( const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs); const rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs); @@ -1804,6 +1976,7 @@ fn boolBinOp( const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const src = token_starts[main_tokens[infix_node]]; const bool_type = try addZIRInstConst(mod, scope, src, .{ @@ -1899,13 +2072,14 @@ fn ifExpr( defer block_scope.instructions.deinit(mod.gpa); const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const if_src = token_starts[if_full.ast.if_token]; const cond = c: { // TODO https://github.com/ziglang/zig/issues/7929 - if (if_full.ast.error_token) |error_token| { + if (if_full.error_token) |error_token| { return mod.failTok(scope, error_token, "TODO implement if error union", .{}); } else if (if_full.payload_token) |payload_token| { return mod.failTok(scope, payload_token, "TODO implement if optional", .{}); @@ -1966,7 +2140,7 @@ fn ifExpr( }; } else .{ - .src = token_starts[tree.lastToken(if_full.then_expr)], + .src = token_starts[tree.lastToken(if_full.ast.then_expr)], .result = null, }; @@ -2042,8 +2216,9 @@ fn whileExpr( defer continue_scope.instructions.deinit(mod.gpa); const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const while_src = token_starts[while_full.ast.while_token]; const void_type = try addZIRInstConst(mod, scope, while_src, .{ .ty = Type.initTag(.type), @@ -2051,16 +2226,16 @@ fn whileExpr( }); const cond = c: { // TODO https://github.com/ziglang/zig/issues/7929 - if (while_full.ast.error_token) |error_token| { + if (while_full.error_token) |error_token| { return mod.failTok(scope, error_token, "TODO implement while error union", .{}); } else if (while_full.payload_token) |payload_token| { return mod.failTok(scope, payload_token, "TODO implement while optional", .{}); } else { - const bool_type = try addZIRInstConst(mod, &block_scope.base, while_src, .{ + const bool_type = try addZIRInstConst(mod, &continue_scope.base, while_src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type), }); - break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr); + break :c try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr); } }; @@ -2128,7 +2303,7 @@ fn whileExpr( }; defer else_scope.instructions.deinit(mod.gpa); - const else_node = if_full.ast.else_expr; + const else_node = while_full.ast.else_expr; const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: { loop_scope.break_count += 1; const sub_scope = &else_scope.base; @@ -2138,7 +2313,7 @@ fn whileExpr( }; } else .{ - .src = token_starts[tree.lastToken(then_node)], + .src = token_starts[tree.lastToken(while_full.ast.then_expr)], .result = null, }; @@ -2181,8 +2356,10 @@ fn forExpr( // Set up variables and constants. const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); + const for_src = token_starts[for_full.ast.while_token]; const index_ptr = blk: { const usize_type = try addZIRInstConst(mod, scope, for_src, .{ @@ -2299,7 +2476,7 @@ fn forExpr( else break :blk &then_scope.base; if (mem.eql(u8, tree.tokenSlice(index_token), "_")) { - return mod.failTok(&then_scope.base, index_token, "discard of index capture not allowed; omit it instead", .{}); + return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{}); } const index_name = try mod.identifierTokenString(&then_scope.base, index_token); index_scope = .{ @@ -2334,7 +2511,7 @@ fn forExpr( }; } else .{ - .src = token_starts[tree.lastToken(then_node)], + .src = token_starts[tree.lastToken(for_full.ast.then_expr)], .result = null, }; @@ -2386,10 +2563,12 @@ fn switchExpr( const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const token_tags = tree.tokens.items(.tag); + const token_starts = tree.tokens.items(.start); + const node_tags = tree.nodes.items(.tag); const switch_token = main_tokens[switch_node]; - const target_node = datas[switch_node].lhs; - const extra = tree.extraData(datas[switch_node].rhs, ast.switch_node.SubRange); + const target_node = node_datas[switch_node].lhs; + const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); const case_nodes = tree.extra_data[extra.start..extra.end]; const switch_src = token_starts[switch_token]; @@ -2552,7 +2731,7 @@ fn switchExpr( defer else_scope.instructions.deinit(mod.gpa); // Now generate all but the special cases. - var special_case: ?ast.Node.Index = null; + var special_case: ?ast.full.SwitchCase = null; var items_index: usize = 0; var case_index: usize = 0; for (case_nodes) |case_node| { @@ -2582,7 +2761,7 @@ fn switchExpr( { const item = items.items[items_index]; items_index += 1; - try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); + try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); cases[case_index] = .{ .item = item, @@ -2638,7 +2817,7 @@ fn switchExpr( // reset cond_scope for then_body case_scope.instructions.items.len = 0; - try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); + try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); condbr.positionals.then_body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), }; @@ -2655,7 +2834,7 @@ fn switchExpr( // Finally generate else block or a break. if (special_case) |case| { - try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); + try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target); } else { // Not handling all possible cases is a compile error. _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); @@ -2674,11 +2853,13 @@ fn switchCaseExpr( block: *zir.Inst.Block, case: ast.full.SwitchCase, target: *zir.Inst, - target_ptr: ?*zir.Inst, ) !void { const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const token_tags = tree.tokens.items(.tag); + const case_src = token_starts[case.ast.arrow_token]; const sub_scope = blk: { const payload_token = case.payload_token orelse break :blk scope; @@ -2690,11 +2871,11 @@ fn switchCaseExpr( const value_name = tree.tokenSlice(ident); if (mem.eql(u8, value_name, "_")) { if (is_ptr) { - return mod.failTok(scope, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); + return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{}); } break :blk scope; } - return mod.failNode(scope, payload.value_symbol, "TODO implement switch value payload", .{}); + return mod.failTok(scope, ident, "TODO implement switch value payload", .{}); }; const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr); @@ -2710,10 +2891,12 @@ fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const src = token_starts[main_tokens[node]]; const rhs_node = node_datas[node].lhs; if (rhs_node != 0) { - if (nodeMayNeedMemoryLocation(rhs_node, scope)) { + if (nodeMayNeedMemoryLocation(scope, rhs_node)) { const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr); const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node); return addZIRUnOp(mod, scope, src, .@"return", operand); @@ -2737,8 +2920,8 @@ fn identifier( defer tracy.end(); const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const ident_token = main_tokens[ident]; const ident_name = try mod.identifierTokenString(scope, ident_token); @@ -2826,6 +3009,27 @@ fn identifier( return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); } +fn parseStringLiteral(mod: *Module, scope: *Scope, token: ast.TokenIndex) ![]u8 { + const tree = scope.tree(); + const token_tags = tree.tokens.items(.tag); + const token_starts = tree.tokens.items(.start); + assert(token_tags[token] == .string_literal); + const unparsed = tree.tokenSlice(token); + const arena = scope.arena(); + var bad_index: usize = undefined; + const bytes = std.zig.parseStringLiteral(arena, unparsed, &bad_index) catch |err| switch (err) { + error.InvalidCharacter => { + const bad_byte = unparsed[bad_index]; + const src = token_starts[token]; + return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'", .{ + bad_byte, + }); + }, + else => |e| return e, + }; + return bytes; +} + fn stringLiteral( mod: *Module, scope: *Scope, @@ -2833,23 +3037,11 @@ fn stringLiteral( str_lit: ast.Node.Index, ) InnerError!*zir.Inst { const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const str_lit_token = main_tokens[str_lit]; - const unparsed_bytes = tree.tokenSlice(str_lit_token); - const arena = scope.arena(); - - var bad_index: usize = undefined; - const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) { - error.InvalidCharacter => { - const bad_byte = unparsed_bytes[bad_index]; - const src = token_starts[str_lit_token]; - return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); - }, - else => |e| return e, - }; - + const bytes = try parseStringLiteral(mod, scope, str_lit_token); const src = token_starts[str_lit_token]; const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); return rvalue(mod, scope, rl, str_inst); @@ -2864,9 +3056,10 @@ fn multilineStringLiteral( const tree = scope.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); - const start = node_datas[node].lhs; - const end = node_datas[node].rhs; + const start = node_datas[str_lit].lhs; + const end = node_datas[str_lit].rhs; // Count the number of bytes to allocate. const len: usize = len: { @@ -2905,9 +3098,10 @@ fn multilineStringLiteral( fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const main_token = main_tokens[node]; + const token_starts = tree.tokens.items(.start); + const src = token_starts[main_token]; const slice = tree.tokenSlice(main_token); @@ -2934,6 +3128,7 @@ fn integerLiteral( const arena = scope.arena(); const tree = scope.tree(); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); const int_token = main_tokens[int_lit]; const prefixed_bytes = tree.tokenSlice(int_token); @@ -2972,6 +3167,8 @@ fn floatLiteral( const arena = scope.arena(); const tree = scope.tree(); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const main_token = main_tokens[float_lit]; const bytes = tree.tokenSlice(main_token); if (bytes.len > 2 and bytes[1] == 'x') { @@ -2988,17 +3185,18 @@ fn floatLiteral( return rvalue(mod, scope, rl, result); } -fn assembly(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) InnerError!*zir.Inst { +fn asmExpr(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const node_datas = tree.nodes.items(.data); if (full.outputs.len != 0) { return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{}); } - const inputs = try arena.alloc(*zir.Inst, full.inputs.len); + const inputs = try arena.alloc([]const u8, full.inputs.len); const args = try arena.alloc(*zir.Inst, full.inputs.len); const src = token_starts[full.ast.asm_token]; @@ -3010,15 +3208,16 @@ fn assembly(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) Inne for (full.inputs) |input, i| { // TODO semantically analyze constraints - inputs[i] = try expr(mod, scope, str_type_rl, input.constraint); - args[i] = try expr(mod, scope, .none, input.expr); + const constraint_token = main_tokens[input] + 2; + inputs[i] = try parseStringLiteral(mod, scope, constraint_token); + args[i] = try expr(mod, scope, .none, node_datas[input].lhs); } const return_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type), }); - const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.@"asm", .{ + const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{ .asm_source = try expr(mod, scope, str_type_rl, full.ast.template), .return_type = return_type, }, .{ @@ -3185,8 +3384,9 @@ fn builtinCall( params: []const ast.Node.Index, ) InnerError!*zir.Inst { const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const builtin_token = main_tokens[call]; const builtin_name = tree.tokenSlice(builtin_token); @@ -3200,11 +3400,13 @@ fn builtinCall( builtin_name, }); }; - if (info.param_count != params.len) { - const s = if (params.len == 1) "" else "s"; - return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{ - expected, s, found, - }); + if (info.param_count) |expected| { + if (expected != params.len) { + const s = if (expected == 1) "" else "s"; + return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{ + expected, s, params.len, + }); + } } const src = token_starts[builtin_token]; @@ -3237,7 +3439,7 @@ fn builtinCall( }, .compile_error => { const target = try expr(mod, scope, .none, params[0]); - const result = addZIRUnOp(mod, scope, src, .compile_error, target); + const result = try addZIRUnOp(mod, scope, src, .compile_error, target); return rvalue(mod, scope, rl, result); }, .set_eval_branch_quota => { @@ -3386,8 +3588,9 @@ fn callExpr( } const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); + const token_starts = tree.tokens.items(.start); + const lhs = try expr(mod, scope, .none, call.ast.fn_expr); const args = try scope.getGenZIR().arena.alloc(*zir.Inst, call.ast.params.len); @@ -3446,23 +3649,26 @@ fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { return null; } -fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { +fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool { + const tree = scope.tree(); + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); + var node = start_node; while (true) { - switch (node.tag) { - .Root, + switch (node_tags[node]) { + .root, .@"usingnamespace", .test_decl, - .doc_comment, .switch_case, - .switch_else, - .Else, - .Payload, - .PointerPayload, - .PointerIndexPayload, - .ContainerField, - .ErrorTag, - .FieldInitializer, + .switch_case_one, + .container_field_init, + .container_field_align, + .container_field, + .asm_output, + .asm_input, => unreachable, .@"return", @@ -3470,8 +3676,12 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .@"continue", .bit_not, .bool_not, - .var_decl, + .global_var_decl, + .local_var_decl, + .simple_var_decl, + .aligned_var_decl, .@"defer", + .@"errdefer", .address_of, .optional_type, .negation, @@ -3479,27 +3689,46 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .@"resume", .array_type, .array_type_sentinel, - .PtrType, - .slice_type, + .ptr_type_aligned, + .ptr_type_sentinel, + .ptr_type, + .ptr_type_bit_range, .@"suspend", .@"anytype", - .error_type, - .FnProto, + .fn_proto_simple, + .fn_proto_multi, + .fn_proto_one, + .fn_proto, + .fn_decl, .anyframe_type, + .anyframe_literal, .integer_literal, .float_literal, .enum_literal, .string_literal, - .MultilineStringLiteral, + .multiline_string_literal, .char_literal, - .bool_literal, + .true_literal, + .false_literal, .null_literal, .undefined_literal, - .@"unreachable", + .unreachable_literal, .identifier, .error_set_decl, - .ContainerDecl, + .container_decl, + .container_decl_comma, + .container_decl_two, + .container_decl_two_comma, + .container_decl_arg, + .container_decl_arg_comma, + .tagged_union, + .tagged_union_comma, + .tagged_union_two, + .tagged_union_two_comma, + .tagged_union_enum_tag, + .tagged_union_enum_tag_comma, .@"asm", + .asm_simple, .add, .add_wrap, .array_cat, @@ -3537,14 +3766,16 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .mod, .mul, .mul_wrap, - .range, - .period, + .switch_range, + .field_access, .sub, .sub_wrap, .slice, + .slice_open, + .slice_sentinel, .deref, .array_access, - .block, + .error_value, .while_simple, // This variant cannot have an else expression. .while_cont, // This variant cannot have an else expression. .for_simple, // This variant cannot have an else expression. @@ -3558,18 +3789,30 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .@"comptime", .@"nosuspend", .unwrap_optional, - => node = datas[node].lhs, + => node = node_datas[node].lhs, // Forward the question to the RHS sub-expression. .@"catch", .@"orelse", - => node = datas[node].rhs, + => node = node_datas[node].rhs, // True because these are exactly the expressions we need memory locations for. - .ArrayInitializer, - .ArrayInitializerDot, - .StructInitializer, - .StructInitializerDot, + .array_init_one, + .array_init_one_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init, + .array_init_comma, + .struct_init_one, + .struct_init_one_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init, + .struct_init_comma, => return true, // True because depending on comptime conditions, sub-expressions @@ -3578,6 +3821,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .@"if", // This variant always has an else expression. .@"for", // This variant always has an else expression. .@"switch", + .switch_comma, .call_one, .call_one_comma, .async_call_one, @@ -3588,10 +3832,10 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { .async_call_comma, => return true, - block_two, - block_two_semicolon, - block, - block_semicolon, + .block_two, + .block_two_semicolon, + .block, + .block_semicolon, => { const lbrace = main_tokens[node]; if (token_tags[lbrace - 1] == .colon) { @@ -3603,7 +3847,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { } }, - .builtin_call => { + .builtin_call, + .builtin_call_comma, + .builtin_call_two, + .builtin_call_two_comma, + => { const builtin_token = main_tokens[node]; const builtin_name = tree.tokenSlice(builtin_token); // If the builtin is an invalid name, we don't cause an error here; instead @@ -3661,7 +3909,6 @@ fn rvalueVoid( result: void, ) InnerError!*zir.Inst { const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const src = tree.tokens.items(.start)[tree.firstToken(node)]; const void_inst = try addZIRInstConst(mod, scope, src, .{ @@ -3765,7 +4012,7 @@ pub fn addZirInstT( src: usize, comptime T: type, tag: zir.Inst.Tag, - positionals: std.meta.fieldInfo(tag.Type(), .positionals).field_type, + positionals: std.meta.fieldInfo(T, .positionals).field_type, ) !*T { const gen_zir = scope.getGenZIR(); try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); diff --git a/src/zir.zig b/src/zir.zig index 9a3c080760..ee0fd3dc3d 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -863,8 +863,8 @@ pub const Inst = struct { kw_args: struct { @"volatile": bool = false, output: ?*Inst = null, - inputs: []*Inst = &[0]*Inst{}, - clobbers: []*Inst = &[0]*Inst{}, + inputs: []const []const u8 = &.{}, + clobbers: []const []const u8 = &.{}, args: []*Inst = &[0]*Inst{}, }, }; @@ -1192,16 +1192,9 @@ pub const Inst = struct { }, kw_args: struct { init_inst: ?*Inst = null, - init_kind: InitKind = .none, + has_enum_token: bool, layout: std.builtin.TypeInfo.ContainerLayout = .Auto, }, - - // TODO error: values of type '(enum literal)' must be comptime known - pub const InitKind = enum { - enum_type, - tag_type, - none, - }; }; pub const SwitchBr = struct { @@ -1413,6 +1406,7 @@ const Writer = struct { } switch (@TypeOf(param)) { *Inst => return self.writeInstParamToStream(stream, param), + ?*Inst => return self.writeInstParamToStream(stream, param.?), []*Inst => { try stream.writeByte('['); for (param) |inst, i| { @@ -1480,7 +1474,7 @@ const Writer = struct { const name = self.loop_table.get(param).?; return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)}); }, - [][]const u8 => { + [][]const u8, []const []const u8 => { try stream.writeByte('['); for (param) |str, i| { if (i != 0) { diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 83d7113c9c..b20e78d448 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -2023,19 +2023,21 @@ fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); + const return_type = try resolveType(mod, scope, assembly.positionals.return_type); const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source); const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null; - const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len); - const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len); - const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len); + const arena = scope.arena(); + const inputs = try arena.alloc([]const u8, assembly.kw_args.inputs.len); + const clobbers = try arena.alloc([]const u8, assembly.kw_args.clobbers.len); + const args = try arena.alloc(*Inst, assembly.kw_args.args.len); for (inputs) |*elem, i| { - elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]); + elem.* = try arena.dupe(u8, assembly.kw_args.inputs[i]); } for (clobbers) |*elem, i| { - elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]); + elem.* = try arena.dupe(u8, assembly.kw_args.clobbers[i]); } for (args) |*elem, i| { const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]); -- cgit v1.2.3 From 8fee41b1d528d598521525574206e200fd332c67 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 19 Feb 2021 18:04:52 -0700 Subject: stage2: AST: clean up parse errors * struct instead of tagged union * delete dead code * simplify parser code * remove unnecessary metaprogramming --- lib/std/zig/ast.zig | 529 ++++++++++++++++---------------------------- lib/std/zig/parse.zig | 338 +++++++++++----------------- lib/std/zig/parser_test.zig | 127 ++++++----- src/Module.zig | 2 +- src/main.zig | 2 +- 5 files changed, 390 insertions(+), 608 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 2042f07cf6..fda2916af4 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -132,113 +132,160 @@ pub const Tree = struct { } pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void { - const tokens = tree.tokens.items(.tag); - switch (parse_error) { - .InvalidToken => |*x| return x.render(tokens, stream), - .ExpectedContainerMembers => |*x| return x.render(tokens, stream), - .ExpectedStringLiteral => |*x| return x.render(tokens, stream), - .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream), - .ExpectedPubItem => |*x| return x.render(tokens, stream), - .ExpectedIdentifier => |*x| return x.render(tokens, stream), - .ExpectedStatement => |*x| return x.render(tokens, stream), - .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), - .ExpectedVarDecl => |*x| return x.render(tokens, stream), - .ExpectedFn => |*x| return x.render(tokens, stream), - .ExpectedReturnType => |*x| return x.render(tokens, stream), - .ExpectedAggregateKw => |*x| return x.render(tokens, stream), - .SameLineDocComment => |*x| return x.render(tokens, stream), - .UnattachedDocComment => |*x| return x.render(tokens, stream), - .ExpectedEqOrSemi => |*x| return x.render(tokens, stream), - .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream), - .ExpectedSemiOrElse => |*x| return x.render(tokens, stream), - .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream), - .ExpectedLBrace => |*x| return x.render(tokens, stream), - .ExpectedColonOrRParen => |*x| return x.render(tokens, stream), - .ExpectedLabelable => |*x| return x.render(tokens, stream), - .ExpectedInlinable => |*x| return x.render(tokens, stream), - .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream), - .ExpectedCall => |x| return x.render(tree, stream), - .ExpectedCallOrFnProto => |x| return x.render(tree, stream), - .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), - .ExtraAlignQualifier => |*x| return x.render(tokens, stream), - .ExtraConstQualifier => |*x| return x.render(tokens, stream), - .ExtraVolatileQualifier => |*x| return x.render(tokens, stream), - .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream), - .ExpectedTypeExpr => |*x| return x.render(tokens, stream), - .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream), - .ExpectedParamType => |*x| return x.render(tokens, stream), - .ExpectedExpr => |*x| return x.render(tokens, stream), - .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), - .ExpectedToken => |*x| return x.render(tokens, stream), - .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), - .ExpectedParamList => |*x| return x.render(tokens, stream), - .ExpectedPayload => |*x| return x.render(tokens, stream), - .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream), - .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream), - .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream), - .ExpectedPrefixExpr => |*x| return x.render(tokens, stream), - .ExpectedLoopExpr => |*x| return x.render(tokens, stream), - .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream), - .ExpectedSuffixOp => |*x| return x.render(tokens, stream), - .ExpectedBlockOrField => |*x| return x.render(tokens, stream), - .DeclBetweenFields => |*x| return x.render(tokens, stream), - .InvalidAnd => |*x| return x.render(tokens, stream), - .AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream), - } - } + const token_tags = tree.tokens.items(.tag); + switch (parse_error.tag) { + .asterisk_after_ptr_deref => { + return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?"); + }, + .decl_between_fields => { + return stream.writeAll("declarations are not allowed between container fields"); + }, + .expected_block => { + return stream.print("expected block or field, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_block_or_assignment => { + return stream.print("expected block or assignment, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_block_or_expr => { + return stream.print("expected block or expression, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_block_or_field => { + return stream.print("expected block or field, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_container_members => { + return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_expr => { + return stream.print("expected expression, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_expr_or_assignment => { + return stream.print("expected expression or assignment, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_fn => { + return stream.print("expected function, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_inlinable => { + return stream.print("expected 'while' or 'for', found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_labelable => { + return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_param_list => { + return stream.print("expected parameter list, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_prefix_expr => { + return stream.print("expected prefix expression, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_primary_type_expr => { + return stream.print("expected primary type expression, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_return_type => { + return stream.print("expected return type expression, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_semi_or_else => { + return stream.print("expected ';' or 'else', found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_semi_or_lbrace => { + return stream.print("expected ';' or '{{', found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_statement => { + return stream.print("expected statement, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_string_literal => { + return stream.print("expected string literal, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_suffix_op => { + return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_type_expr => { + return stream.print("expected type expression, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_var_decl => { + return stream.print("expected variable declaration, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .expected_var_decl_or_fn => { + return stream.print("expected variable declaration or function, found '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .extra_align_qualifier => { + return stream.writeAll("extra align qualifier"); + }, + .extra_allowzero_qualifier => { + return stream.writeAll("extra allowzero qualifier"); + }, + .extra_const_qualifier => { + return stream.writeAll("extra const qualifier"); + }, + .extra_volatile_qualifier => { + return stream.writeAll("extra volatile qualifier"); + }, + .invalid_token => { + return stream.print("invalid token '{s}'", .{ + token_tags[parse_error.token].symbol(), + }); + }, + .same_line_doc_comment => { + return stream.writeAll("same line documentation comment"); + }, + .unattached_doc_comment => { + return stream.writeAll("unattached documentation comment"); + }, - pub fn errorToken(tree: Tree, parse_error: Error) TokenIndex { - switch (parse_error) { - .InvalidToken => |x| return x.token, - .ExpectedContainerMembers => |x| return x.token, - .ExpectedStringLiteral => |x| return x.token, - .ExpectedIntegerLiteral => |x| return x.token, - .ExpectedPubItem => |x| return x.token, - .ExpectedIdentifier => |x| return x.token, - .ExpectedStatement => |x| return x.token, - .ExpectedVarDeclOrFn => |x| return x.token, - .ExpectedVarDecl => |x| return x.token, - .ExpectedFn => |x| return x.token, - .ExpectedReturnType => |x| return x.token, - .ExpectedAggregateKw => |x| return x.token, - .SameLineDocComment => |x| return x.token, - .UnattachedDocComment => |x| return x.token, - .ExpectedEqOrSemi => |x| return x.token, - .ExpectedSemiOrLBrace => |x| return x.token, - .ExpectedSemiOrElse => |x| return x.token, - .ExpectedLabelOrLBrace => |x| return x.token, - .ExpectedLBrace => |x| return x.token, - .ExpectedColonOrRParen => |x| return x.token, - .ExpectedLabelable => |x| return x.token, - .ExpectedInlinable => |x| return x.token, - .ExpectedAsmOutputReturnOrType => |x| return x.token, - .ExpectedCall => |x| return tree.nodes.items(.main_token)[x.node], - .ExpectedCallOrFnProto => |x| return tree.nodes.items(.main_token)[x.node], - .ExpectedSliceOrRBracket => |x| return x.token, - .ExtraAlignQualifier => |x| return x.token, - .ExtraConstQualifier => |x| return x.token, - .ExtraVolatileQualifier => |x| return x.token, - .ExtraAllowZeroQualifier => |x| return x.token, - .ExpectedTypeExpr => |x| return x.token, - .ExpectedPrimaryTypeExpr => |x| return x.token, - .ExpectedParamType => |x| return x.token, - .ExpectedExpr => |x| return x.token, - .ExpectedPrimaryExpr => |x| return x.token, - .ExpectedToken => |x| return x.token, - .ExpectedCommaOrEnd => |x| return x.token, - .ExpectedParamList => |x| return x.token, - .ExpectedPayload => |x| return x.token, - .ExpectedBlockOrAssignment => |x| return x.token, - .ExpectedBlockOrExpression => |x| return x.token, - .ExpectedExprOrAssignment => |x| return x.token, - .ExpectedPrefixExpr => |x| return x.token, - .ExpectedLoopExpr => |x| return x.token, - .ExpectedDerefOrUnwrap => |x| return x.token, - .ExpectedSuffixOp => |x| return x.token, - .ExpectedBlockOrField => |x| return x.token, - .DeclBetweenFields => |x| return x.token, - .InvalidAnd => |x| return x.token, - .AsteriskAfterPointerDereference => |x| return x.token, + .expected_token => { + const found_tag = token_tags[parse_error.token]; + const expected_symbol = parse_error.extra.expected_tag.symbol(); + switch (found_tag) { + .invalid => return stream.print("expected '{s}', found invalid bytes", .{ + expected_symbol, + }), + else => return stream.print("expected '{s}', found '{s}'", .{ + expected_symbol, found_tag.symbol(), + }), + } + }, } } @@ -2239,236 +2286,50 @@ pub const full = struct { }; }; -pub const Error = union(enum) { - InvalidToken: InvalidToken, - ExpectedContainerMembers: ExpectedContainerMembers, - ExpectedStringLiteral: ExpectedStringLiteral, - ExpectedIntegerLiteral: ExpectedIntegerLiteral, - ExpectedPubItem: ExpectedPubItem, - ExpectedIdentifier: ExpectedIdentifier, - ExpectedStatement: ExpectedStatement, - ExpectedVarDeclOrFn: ExpectedVarDeclOrFn, - ExpectedVarDecl: ExpectedVarDecl, - ExpectedFn: ExpectedFn, - ExpectedReturnType: ExpectedReturnType, - ExpectedAggregateKw: ExpectedAggregateKw, - SameLineDocComment: SameLineDocComment, - UnattachedDocComment: UnattachedDocComment, - ExpectedEqOrSemi: ExpectedEqOrSemi, - ExpectedSemiOrLBrace: ExpectedSemiOrLBrace, - ExpectedSemiOrElse: ExpectedSemiOrElse, - ExpectedLabelOrLBrace: ExpectedLabelOrLBrace, - ExpectedLBrace: ExpectedLBrace, - ExpectedColonOrRParen: ExpectedColonOrRParen, - ExpectedLabelable: ExpectedLabelable, - ExpectedInlinable: ExpectedInlinable, - ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType, - ExpectedCall: ExpectedCall, - ExpectedCallOrFnProto: ExpectedCallOrFnProto, - ExpectedSliceOrRBracket: ExpectedSliceOrRBracket, - ExtraAlignQualifier: ExtraAlignQualifier, - ExtraConstQualifier: ExtraConstQualifier, - ExtraVolatileQualifier: ExtraVolatileQualifier, - ExtraAllowZeroQualifier: ExtraAllowZeroQualifier, - ExpectedTypeExpr: ExpectedTypeExpr, - ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr, - ExpectedParamType: ExpectedParamType, - ExpectedExpr: ExpectedExpr, - ExpectedPrimaryExpr: ExpectedPrimaryExpr, - ExpectedToken: ExpectedToken, - ExpectedCommaOrEnd: ExpectedCommaOrEnd, - ExpectedParamList: ExpectedParamList, - ExpectedPayload: ExpectedPayload, - ExpectedBlockOrAssignment: ExpectedBlockOrAssignment, - ExpectedBlockOrExpression: ExpectedBlockOrExpression, - ExpectedExprOrAssignment: ExpectedExprOrAssignment, - ExpectedPrefixExpr: ExpectedPrefixExpr, - ExpectedLoopExpr: ExpectedLoopExpr, - ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap, - ExpectedSuffixOp: ExpectedSuffixOp, - ExpectedBlockOrField: ExpectedBlockOrField, - DeclBetweenFields: DeclBetweenFields, - InvalidAnd: InvalidAnd, - AsteriskAfterPointerDereference: AsteriskAfterPointerDereference, - - pub const InvalidToken = SingleTokenError("Invalid token '{s}'"); - pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'"); - pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'"); - pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'"); - pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'"); - pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'"); - pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'"); - pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'"); - pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'"); - pub const ExpectedReturnType = SingleTokenError("Expected return type expression, found '{s}'"); - pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Tag.keyword_struct.symbol() ++ "', '" ++ Token.Tag.keyword_union.symbol() ++ "', '" ++ Token.Tag.keyword_enum.symbol() ++ "', or '" ++ Token.Tag.keyword_opaque.symbol() ++ "', found '{s}'"); - pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'"); - pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'"); - pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'"); - pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'"); - pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'"); - pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'"); - pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'"); - pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'"); - pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Tag.identifier.symbol() ++ "', found '{s}'"); - pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'"); - pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'"); - pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'"); - pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'"); - pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'"); - pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'"); - pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'"); - pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'"); - pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'"); - pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'"); - pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'"); - pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'"); - pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'"); - pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'"); - pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'"); - - pub const ExpectedParamType = SimpleError("Expected parameter type"); - pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); - pub const SameLineDocComment = SimpleError("Same line documentation comment"); - pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); - pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); - pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); - pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); - pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier"); - pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields"); - pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND."); - pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?"); - - pub const ExpectedCall = struct { - node: Node.Index, - - pub fn render(self: ExpectedCall, tree: Tree, stream: anytype) !void { - const node_tag = tree.nodes.items(.tag)[self.node]; - return stream.print("expected " ++ @tagName(Node.Tag.call) ++ ", found {s}", .{ - @tagName(node_tag), - }); - } - }; - - pub const ExpectedCallOrFnProto = struct { - node: Node.Index, - - pub fn render(self: ExpectedCallOrFnProto, tree: Tree, stream: anytype) !void { - const node_tag = tree.nodes.items(.tag)[self.node]; - return stream.print("expected " ++ @tagName(Node.Tag.call) ++ " or " ++ - @tagName(Node.Tag.fn_proto) ++ ", found {s}", .{@tagName(node_tag)}); - } - }; - - pub const ExpectedToken = struct { - token: TokenIndex, - expected_id: Token.Tag, - - pub fn render(self: *const ExpectedToken, tokens: []const Token.Tag, stream: anytype) !void { - const found_token = tokens[self.token]; - switch (found_token) { - .invalid => { - return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); - }, - else => { - const token_name = found_token.symbol(); - return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); - }, - } - } - }; - - pub const ExpectedCommaOrEnd = struct { - token: TokenIndex, - end_id: Token.Tag, +pub const Error = struct { + tag: Tag, + token: TokenIndex, + extra: union { + none: void, + expected_tag: Token.Tag, + } = .{ .none = {} }, - pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Tag, stream: anytype) !void { - const actual_token = tokens[self.token]; - return stream.print("expected ',' or '{s}', found '{s}'", .{ - self.end_id.symbol(), - actual_token.symbol(), - }); - } + pub const Tag = enum { + asterisk_after_ptr_deref, + decl_between_fields, + expected_block, + expected_block_or_assignment, + expected_block_or_expr, + expected_block_or_field, + expected_container_members, + expected_expr, + expected_expr_or_assignment, + expected_fn, + expected_inlinable, + expected_labelable, + expected_param_list, + expected_prefix_expr, + expected_primary_type_expr, + expected_return_type, + expected_semi_or_else, + expected_semi_or_lbrace, + expected_statement, + expected_string_literal, + expected_suffix_op, + expected_type_expr, + expected_var_decl, + expected_var_decl_or_fn, + extra_align_qualifier, + extra_allowzero_qualifier, + extra_const_qualifier, + extra_volatile_qualifier, + invalid_token, + same_line_doc_comment, + unattached_doc_comment, + + /// `expected_tag` is populated. + expected_token, }; - - fn SingleTokenError(comptime msg: []const u8) type { - return struct { - const ThisError = @This(); - - token: TokenIndex, - - pub fn render(self: *const ThisError, tokens: []const Token.Tag, stream: anytype) !void { - const actual_token = tokens[self.token]; - return stream.print(msg, .{actual_token.symbol()}); - } - }; - } - - fn SimpleError(comptime msg: []const u8) type { - return struct { - const ThisError = @This(); - - token: TokenIndex, - - pub fn render(self: *const ThisError, tokens: []const Token.Tag, stream: anytype) !void { - return stream.writeAll(msg); - } - }; - } - - pub fn loc(self: Error) TokenIndex { - switch (self) { - .InvalidToken => |x| return x.token, - .ExpectedContainerMembers => |x| return x.token, - .ExpectedStringLiteral => |x| return x.token, - .ExpectedIntegerLiteral => |x| return x.token, - .ExpectedPubItem => |x| return x.token, - .ExpectedIdentifier => |x| return x.token, - .ExpectedStatement => |x| return x.token, - .ExpectedVarDeclOrFn => |x| return x.token, - .ExpectedVarDecl => |x| return x.token, - .ExpectedFn => |x| return x.token, - .ExpectedReturnType => |x| return x.token, - .ExpectedAggregateKw => |x| return x.token, - .UnattachedDocComment => |x| return x.token, - .ExpectedEqOrSemi => |x| return x.token, - .ExpectedSemiOrLBrace => |x| return x.token, - .ExpectedSemiOrElse => |x| return x.token, - .ExpectedLabelOrLBrace => |x| return x.token, - .ExpectedLBrace => |x| return x.token, - .ExpectedColonOrRParen => |x| return x.token, - .ExpectedLabelable => |x| return x.token, - .ExpectedInlinable => |x| return x.token, - .ExpectedAsmOutputReturnOrType => |x| return x.token, - .ExpectedCall => |x| @panic("TODO redo ast errors"), - .ExpectedCallOrFnProto => |x| @panic("TODO redo ast errors"), - .ExpectedSliceOrRBracket => |x| return x.token, - .ExtraAlignQualifier => |x| return x.token, - .ExtraConstQualifier => |x| return x.token, - .ExtraVolatileQualifier => |x| return x.token, - .ExtraAllowZeroQualifier => |x| return x.token, - .ExpectedTypeExpr => |x| return x.token, - .ExpectedPrimaryTypeExpr => |x| return x.token, - .ExpectedParamType => |x| return x.token, - .ExpectedExpr => |x| return x.token, - .ExpectedPrimaryExpr => |x| return x.token, - .ExpectedToken => |x| return x.token, - .ExpectedCommaOrEnd => |x| return x.token, - .ExpectedParamList => |x| return x.token, - .ExpectedPayload => |x| return x.token, - .ExpectedBlockOrAssignment => |x| return x.token, - .ExpectedBlockOrExpression => |x| return x.token, - .ExpectedExprOrAssignment => |x| return x.token, - .ExpectedPrefixExpr => |x| return x.token, - .ExpectedLoopExpr => |x| return x.token, - .ExpectedDerefOrUnwrap => |x| return x.token, - .ExpectedSuffixOp => |x| return x.token, - .ExpectedBlockOrField => |x| return x.token, - .DeclBetweenFields => |x| return x.token, - .InvalidAnd => |x| return x.token, - .AsteriskAfterPointerDereference => |x| return x.token, - } - } }; pub const Node = struct { diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index f6545c0f13..a557b57ad4 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -150,14 +150,41 @@ const Parser = struct { return result; } - fn warn(p: *Parser, msg: ast.Error) error{OutOfMemory}!void { + fn warn(p: *Parser, tag: ast.Error.Tag) error{OutOfMemory}!void { + @setCold(true); + try p.warnMsg(.{ .tag = tag, .token = p.tok_i }); + } + + fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void { + @setCold(true); + try p.warnMsg(.{ + .tag = .expected_token, + .token = p.tok_i, + .extra = .{ .expected_tag = expected_token }, + }); + } + fn warnMsg(p: *Parser, msg: ast.Error) error{OutOfMemory}!void { @setCold(true); try p.errors.append(p.gpa, msg); } - fn fail(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } { + fn fail(p: *Parser, tag: ast.Error.Tag) error{ ParseError, OutOfMemory } { + @setCold(true); + return p.failMsg(.{ .tag = tag, .token = p.tok_i }); + } + + fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } { @setCold(true); - try p.warn(msg); + return p.failMsg(.{ + .tag = .expected_token, + .token = p.tok_i, + .extra = .{ .expected_tag = expected_token }, + }); + } + + fn failMsg(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } { + @setCold(true); + try p.warnMsg(msg); return error.ParseError; } @@ -190,7 +217,7 @@ const Parser = struct { var trailing_comma = false; while (true) { - const doc_comment = try p.eatDocComments (); + const doc_comment = try p.eatDocComments(); switch (p.token_tags[p.tok_i]) { .keyword_test => { @@ -212,8 +239,9 @@ const Parser = struct { .none => field_state = .seen, .err, .seen => {}, .end => |node| { - try p.warn(.{ - .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] }, + try p.warnMsg(.{ + .tag = .decl_between_fields, + .token = p.nodes.items(.main_token)[node], }); // Continue parsing; error will be reported later. field_state = .err; @@ -234,9 +262,7 @@ const Parser = struct { } // There is not allowed to be a decl after a field with no comma. // Report error but recover parser. - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); p.findNextContainerMember(); } }, @@ -267,7 +293,7 @@ const Parser = struct { }, else => { p.tok_i += 1; - try p.warn(.{ .ExpectedBlockOrField = .{ .token = p.tok_i } }); + try p.warn(.expected_block_or_field); }, }, .keyword_pub => { @@ -316,8 +342,9 @@ const Parser = struct { .none => field_state = .seen, .err, .seen => {}, .end => |node| { - try p.warn(.{ - .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] }, + try p.warnMsg(.{ + .tag = .decl_between_fields, + .token = p.nodes.items(.main_token)[node], }); // Continue parsing; error will be reported later. field_state = .err; @@ -338,20 +365,21 @@ const Parser = struct { } // There is not allowed to be a decl after a field with no comma. // Report error but recover parser. - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); p.findNextContainerMember(); } }, .eof, .r_brace => { if (doc_comment) |tok| { - try p.warn(.{ .UnattachedDocComment = .{ .token = tok } }); + try p.warnMsg(.{ + .tag = .unattached_doc_comment, + .token = tok, + }); } break; }, else => { - try p.warn(.{ .ExpectedContainerMembers = .{ .token = p.tok_i } }); + try p.warn(.expected_container_members); // This was likely not supposed to end yet; try to find the next declaration. p.findNextContainerMember(); }, @@ -475,7 +503,7 @@ const Parser = struct { const test_token = p.assertToken(.keyword_test); const name_token = p.eatToken(.string_literal); const block_node = try p.parseBlock(); - if (block_node == 0) return p.fail(.{ .ExpectedLBrace = .{ .token = p.tok_i } }); + if (block_node == 0) return p.fail(.expected_block); return p.addNode(.{ .tag = .test_decl, .main_token = test_token, @@ -540,15 +568,13 @@ const Parser = struct { // Since parseBlock only return error.ParseError on // a missing '}' we can assume this function was // supposed to end here. - try p.warn(.{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } }); + try p.warn(.expected_semi_or_lbrace); return null_node; }, } } if (expect_fn) { - try p.warn(.{ - .ExpectedFn = .{ .token = p.tok_i }, - }); + try p.warn(.expected_fn); return error.ParseError; } @@ -559,11 +585,11 @@ const Parser = struct { return var_decl; } if (thread_local_token != null) { - return p.fail(.{ .ExpectedVarDecl = .{ .token = p.tok_i } }); + return p.fail(.expected_var_decl); } if (exported) { - return p.fail(.{ .ExpectedVarDeclOrFn = .{ .token = p.tok_i } }); + return p.fail(.expected_var_decl_or_fn); } return p.expectUsingNamespace(); @@ -618,7 +644,7 @@ const Parser = struct { if (return_type_expr == 0) { // most likely the user forgot to specify the return type. // Mark return type as invalid and try to continue. - try p.warn(.{ .ExpectedReturnType = .{ .token = p.tok_i } }); + try p.warn(.expected_return_type); } if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { @@ -901,7 +927,7 @@ const Parser = struct { fn expectStatement(p: *Parser) !Node.Index { const statement = try p.parseStatement(); if (statement == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.expected_statement); } return statement; } @@ -940,7 +966,7 @@ const Parser = struct { if (block_expr != 0) break :blk block_expr; const assign_expr = try p.parseAssignExpr(); if (assign_expr == 0) { - return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } }); + return p.fail(.expected_block_or_assignment); } if (p.eatToken(.semicolon)) |_| { return p.addNode(.{ @@ -957,7 +983,7 @@ const Parser = struct { }; const else_token = p.eatToken(.keyword_else) orelse { if (else_required) { - return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } }); + return p.fail(.expected_semi_or_else); } return p.addNode(.{ .tag = .if_simple, @@ -993,7 +1019,7 @@ const Parser = struct { if (loop_stmt != 0) return loop_stmt; if (label_token != 0) { - return p.fail(.{ .ExpectedLabelable = .{ .token = p.tok_i } }); + return p.fail(.expected_labelable); } return null_node; @@ -1012,7 +1038,7 @@ const Parser = struct { if (inline_token == null) return null_node; // If we've seen "inline", there should have been a "for" or "while" - return p.fail(.{ .ExpectedInlinable = .{ .token = p.tok_i } }); + return p.fail(.expected_inlinable); } /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload @@ -1034,7 +1060,7 @@ const Parser = struct { if (block_expr != 0) break :blk block_expr; const assign_expr = try p.parseAssignExpr(); if (assign_expr == 0) { - return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } }); + return p.fail(.expected_block_or_assignment); } if (p.eatToken(.semicolon)) |_| { return p.addNode(.{ @@ -1051,7 +1077,7 @@ const Parser = struct { }; const else_token = p.eatToken(.keyword_else) orelse { if (else_required) { - return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } }); + return p.fail(.expected_semi_or_else); } return p.addNode(.{ .tag = .for_simple, @@ -1095,7 +1121,7 @@ const Parser = struct { if (block_expr != 0) break :blk block_expr; const assign_expr = try p.parseAssignExpr(); if (assign_expr == 0) { - return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } }); + return p.fail(.expected_block_or_assignment); } if (p.eatToken(.semicolon)) |_| { if (cont_expr == 0) { @@ -1126,7 +1152,7 @@ const Parser = struct { }; const else_token = p.eatToken(.keyword_else) orelse { if (else_required) { - return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } }); + return p.fail(.expected_semi_or_else); } if (cont_expr == 0) { return p.addNode(.{ @@ -1186,7 +1212,7 @@ const Parser = struct { fn expectBlockExprStatement(p: *Parser) !Node.Index { const node = try p.parseBlockExprStatement(); if (node == 0) { - return p.fail(.{ .ExpectedBlockOrExpression = .{ .token = p.tok_i } }); + return p.fail(.expected_block_or_expr); } return node; } @@ -1259,7 +1285,7 @@ const Parser = struct { fn expectAssignExpr(p: *Parser) !Node.Index { const expr = try p.parseAssignExpr(); if (expr == 0) { - return p.fail(.{ .ExpectedExprOrAssignment = .{ .token = p.tok_i } }); + return p.fail(.expected_expr_or_assignment); } return expr; } @@ -1272,7 +1298,7 @@ const Parser = struct { fn expectExpr(p: *Parser) Error!Node.Index { const node = try p.parseExpr(); if (node == 0) { - return p.fail(.{ .ExpectedExpr = .{ .token = p.tok_i } }); + return p.fail(.expected_expr); } else { return node; } @@ -1289,7 +1315,7 @@ const Parser = struct { const or_token = p.nextToken(); const rhs = try p.parseBoolAndExpr(); if (rhs == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } res = try p.addNode(.{ .tag = .bool_or, @@ -1316,7 +1342,7 @@ const Parser = struct { const and_token = p.nextToken(); const rhs = try p.parseCompareExpr(); if (rhs == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } res = try p.addNode(.{ .tag = .bool_and, @@ -1385,7 +1411,7 @@ const Parser = struct { _ = try p.parsePayload(); const rhs = try p.parseBitShiftExpr(); if (rhs == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } res = try p.addNode(.{ .tag = .@"catch", @@ -1413,7 +1439,7 @@ const Parser = struct { fn expectBitwiseExpr(p: *Parser) Error!Node.Index { const node = try p.parseBitwiseExpr(); if (node == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } else { return node; } @@ -1447,7 +1473,7 @@ const Parser = struct { fn expectBitShiftExpr(p: *Parser) Error!Node.Index { const node = try p.parseBitShiftExpr(); if (node == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } else { return node; } @@ -1487,7 +1513,7 @@ const Parser = struct { fn expectAdditionExpr(p: *Parser) Error!Node.Index { const node = try p.parseAdditionExpr(); if (node == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } return node; } @@ -1528,7 +1554,7 @@ const Parser = struct { fn expectMultiplyExpr(p: *Parser) Error!Node.Index { const node = try p.parseMultiplyExpr(); if (node == 0) { - return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + return p.fail(.invalid_token); } return node; } @@ -1566,7 +1592,7 @@ const Parser = struct { fn expectPrefixExpr(p: *Parser) Error!Node.Index { const node = try p.parsePrefixExpr(); if (node == 0) { - return p.fail(.{ .ExpectedPrefixExpr = .{ .token = p.tok_i } }); + return p.fail(.expected_prefix_expr); } return node; } @@ -1827,7 +1853,7 @@ const Parser = struct { fn expectTypeExpr(p: *Parser) Error!Node.Index { const node = try p.parseTypeExpr(); if (node == 0) { - return p.fail(.{ .ExpectedTypeExpr = .{ .token = p.tok_i } }); + return p.fail(.expected_type_expr); } return node; } @@ -1922,9 +1948,7 @@ const Parser = struct { switch (p.token_tags[p.tok_i]) { .keyword_for => return p.parseForExpr(), .keyword_while => return p.parseWhileExpr(), - else => return p.fail(.{ - .ExpectedInlinable = .{ .token = p.tok_i }, - }), + else => return p.fail(.expected_inlinable), } }, .keyword_for => { @@ -1950,9 +1974,7 @@ const Parser = struct { switch (p.token_tags[p.tok_i]) { .keyword_for => return p.parseForExpr(), .keyword_while => return p.parseWhileExpr(), - else => return p.fail(.{ - .ExpectedInlinable = .{ .token = p.tok_i }, - }), + else => return p.fail(.expected_inlinable), } }, .keyword_for => return p.parseForExpr(), @@ -2170,20 +2192,13 @@ const Parser = struct { .r_brace => break, .colon, .r_paren, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .r_brace, - }, - }); + return p.failExpected(.r_brace); }, else => { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -2214,9 +2229,7 @@ const Parser = struct { }); } if (comma_one == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } var init_list = std.ArrayList(Node.Index).init(p.gpa); @@ -2278,7 +2291,7 @@ const Parser = struct { res = node; } const lparen = (try p.expectTokenRecoverable(.l_paren)) orelse { - try p.warn(.{ .ExpectedParamList = .{ .token = p.tok_i } }); + try p.warn(.expected_param_list); return res; }; if (p.eatToken(.r_paren)) |_| { @@ -2304,9 +2317,7 @@ const Parser = struct { }); } if (comma_one == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } var param_list = std.ArrayList(Node.Index).init(p.gpa); @@ -2352,21 +2363,11 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .r_paren, - }, - }); + return p.failExpected(.r_paren); }, else => { p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .comma, - }, - }); + try p.warnExpected(.comma); }, } } @@ -2405,9 +2406,7 @@ const Parser = struct { }); } if (comma_one == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } var param_list = std.ArrayList(Node.Index).init(p.gpa); @@ -2453,21 +2452,11 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .r_paren, - }, - }); + return p.failExpected(.r_paren); }, else => { p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .comma, - }, - }); + try p.warnExpected(.comma); }, } } @@ -2645,9 +2634,7 @@ const Parser = struct { switch (p.token_tags[p.tok_i]) { .keyword_for => return p.parseForTypeExpr(), .keyword_while => return p.parseWhileTypeExpr(), - else => return p.fail(.{ - .ExpectedInlinable = .{ .token = p.tok_i }, - }), + else => return p.fail(.expected_inlinable), } }, .keyword_for => { @@ -2716,9 +2703,7 @@ const Parser = struct { }); } if (comma_one == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } const field_init_two = try p.expectFieldInit(); const comma_two = p.eatToken(.comma); @@ -2733,9 +2718,7 @@ const Parser = struct { }); } if (comma_two == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } var init_list = std.ArrayList(Node.Index).init(p.gpa); defer init_list.deinit(); @@ -2754,21 +2737,11 @@ const Parser = struct { .r_brace => break, .colon, .r_paren, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .r_brace, - }, - }); + return p.failExpected(.r_brace); }, else => { p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .comma, - }, - }); + try p.warnExpected(.comma); }, } } @@ -2797,9 +2770,7 @@ const Parser = struct { }); } if (comma_one == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } const elem_init_two = try p.expectExpr(); const comma_two = p.eatToken(.comma); @@ -2814,9 +2785,7 @@ const Parser = struct { }); } if (comma_two == null) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); } var init_list = std.ArrayList(Node.Index).init(p.gpa); defer init_list.deinit(); @@ -2835,21 +2804,11 @@ const Parser = struct { .r_brace => break, .colon, .r_paren, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .r_brace, - }, - }); + return p.failExpected(.r_brace); }, else => { p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .comma, - }, - }); + try p.warnExpected(.comma); }, } } @@ -2892,20 +2851,13 @@ const Parser = struct { .r_brace => break, .colon, .r_paren, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ - .token = p.tok_i, - .expected_id = .r_brace, - }, - }); + return p.failExpected(.r_brace); }, else => { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -2942,7 +2894,7 @@ const Parser = struct { fn expectPrimaryTypeExpr(p: *Parser) !Node.Index { const node = try p.parsePrimaryTypeExpr(); if (node == 0) { - return p.fail(.{ .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i } }); + return p.fail(.expected_primary_type_expr); } return node; } @@ -3095,9 +3047,7 @@ const Parser = struct { else => { // This is likely just a missing comma; // give an error but continue parsing this list. - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -3112,9 +3062,7 @@ const Parser = struct { else => { // This is likely just a missing comma; // give an error but continue parsing this list. - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -3126,9 +3074,7 @@ const Parser = struct { else => { // This is likely just a missing comma; // give an error but continue parsing this list. - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -3238,7 +3184,7 @@ const Parser = struct { _ = p.eatToken(.colon) orelse return null_node; _ = try p.expectToken(.l_paren); const node = try p.parseAssignExpr(); - if (node == 0) return p.fail(.{ .ExpectedExprOrAssignment = .{ .token = p.tok_i } }); + if (node == 0) return p.fail(.expected_expr_or_assignment); _ = try p.expectToken(.r_paren); return node; } @@ -3418,9 +3364,7 @@ const Parser = struct { switch (p.token_tags[p.tok_i]) { .keyword_align => { if (result.align_node != 0) { - try p.warn(.{ - .ExtraAlignQualifier = .{ .token = p.tok_i }, - }); + try p.warn(.extra_align_qualifier); } p.tok_i += 1; _ = try p.expectToken(.l_paren); @@ -3436,27 +3380,21 @@ const Parser = struct { }, .keyword_const => { if (saw_const) { - try p.warn(.{ - .ExtraConstQualifier = .{ .token = p.tok_i }, - }); + try p.warn(.extra_const_qualifier); } p.tok_i += 1; saw_const = true; }, .keyword_volatile => { if (saw_volatile) { - try p.warn(.{ - .ExtraVolatileQualifier = .{ .token = p.tok_i }, - }); + try p.warn(.extra_volatile_qualifier); } p.tok_i += 1; saw_volatile = true; }, .keyword_allowzero => { if (saw_allowzero) { - try p.warn(.{ - .ExtraAllowZeroQualifier = .{ .token = p.tok_i }, - }); + try p.warn(.extra_allowzero_qualifier); } p.tok_i += 1; saw_allowzero = true; @@ -3539,11 +3477,10 @@ const Parser = struct { }, }), .invalid_periodasterisks => { - const period_asterisk = p.nextToken(); - try p.warn(.{ .AsteriskAfterPointerDereference = .{ .token = period_asterisk } }); + try p.warn(.asterisk_after_ptr_deref); return p.addNode(.{ .tag = .deref, - .main_token = period_asterisk, + .main_token = p.nextToken(), .data = .{ .lhs = lhs, .rhs = undefined, @@ -3569,7 +3506,7 @@ const Parser = struct { }), else => { p.tok_i += 1; - try p.warn(.{ .ExpectedSuffixOp = .{ .token = p.tok_i } }); + try p.warn(.expected_suffix_op); return null_node; }, }, @@ -3743,9 +3680,7 @@ const Parser = struct { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } else unreachable; @@ -3763,17 +3698,13 @@ const Parser = struct { .r_paren => return SmallSpan{ .zero_or_one = param_one }, .colon, .r_brace, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .r_paren }, - }); + return p.failExpected(.r_paren); }, else => { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } else unreachable; @@ -3799,17 +3730,13 @@ const Parser = struct { .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() }, .colon, .r_brace, .r_bracket => { p.tok_i -= 1; - return p.fail(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .r_paren }, - }); + return p.failExpected(.r_paren); }, else => { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -3836,9 +3763,7 @@ const Parser = struct { else => { // This is likely just a missing comma; // give an error but continue parsing this list. - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -3852,9 +3777,7 @@ const Parser = struct { fn parseBuiltinCall(p: *Parser) !Node.Index { const builtin_token = p.assertToken(.builtin); _ = (try p.expectTokenRecoverable(.l_paren)) orelse { - try p.warn(.{ - .ExpectedParamList = .{ .token = p.tok_i }, - }); + try p.warn(.expected_param_list); // Pretend this was an identifier so we can continue parsing. return p.addNode(.{ .tag = .identifier, @@ -3901,9 +3824,7 @@ const Parser = struct { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } const param_two = try p.expectExpr(); @@ -3932,9 +3853,7 @@ const Parser = struct { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } @@ -3976,9 +3895,7 @@ const Parser = struct { // This is likely just a missing comma; // give an error but continue parsing this list. p.tok_i -= 1; - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma }, - }); + try p.warnExpected(.comma); }, } } @@ -4019,7 +3936,7 @@ const Parser = struct { fn expectStringLiteral(p: *Parser) !Node.Index { const node = try p.parseStringLiteral(); if (node == 0) { - return p.fail(.{ .ExpectedStringLiteral = .{ .token = p.tok_i } }); + return p.fail(.expected_string_literal); } return node; } @@ -4044,7 +3961,7 @@ const Parser = struct { const then_payload = try p.parsePtrPayload(); const then_expr = try bodyParseFn(p); - if (then_expr == 0) return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + if (then_expr == 0) return p.fail(.invalid_token); const else_token = p.eatToken(.keyword_else) orelse return p.addNode(.{ .tag = .if_simple, @@ -4056,7 +3973,7 @@ const Parser = struct { }); const else_payload = try p.parsePayload(); const else_expr = try bodyParseFn(p); - if (else_expr == 0) return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } }); + if (else_expr == 0) return p.fail(.invalid_token); return p.addNode(.{ .tag = .@"if", @@ -4076,7 +3993,10 @@ const Parser = struct { if (p.eatToken(.doc_comment)) |tok| { var first_line = tok; if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) { - try p.warn(.{ .SameLineDocComment = .{ .token = tok } }); + try p.warnMsg(.{ + .tag = .same_line_doc_comment, + .token = tok, + }); first_line = p.eatToken(.doc_comment) orelse return null; } while (p.eatToken(.doc_comment)) |_| {} @@ -4102,16 +4022,18 @@ const Parser = struct { fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex { const token = p.nextToken(); if (p.token_tags[token] != tag) { - return p.fail(.{ .ExpectedToken = .{ .token = token, .expected_id = tag } }); + return p.failMsg(.{ + .tag = .expected_token, + .token = token, + .extra = .{ .expected_tag = tag }, + }); } return token; } fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex { if (p.token_tags[p.tok_i] != tag) { - try p.warn(.{ - .ExpectedToken = .{ .token = p.tok_i, .expected_id = tag }, - }); + try p.warnExpected(tag); return null; } else { return p.nextToken(); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index f8e992bb2e..dc653047df 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -127,7 +127,7 @@ test "zig fmt: decl between fields" { \\ b: usize, \\}; , &[_]Error{ - .DeclBetweenFields, + .decl_between_fields, }); } @@ -135,7 +135,7 @@ test "zig fmt: eof after missing comma" { try testError( \\foo() , &[_]Error{ - .ExpectedToken, + .expected_token, }); } @@ -3578,7 +3578,7 @@ test "zig fmt: file ends with struct field" { // \\const container = extern {}; // \\ // , &[_]Error{ -// .ExpectedExpr, +// .expected_expr, // .ExpectedVarDeclOrFn, // }); //} @@ -3598,12 +3598,12 @@ test "zig fmt: same line doc comment returns error" { \\/// comment \\ , &[_]Error{ - .SameLineDocComment, - .SameLineDocComment, - .UnattachedDocComment, - .SameLineDocComment, - .SameLineDocComment, - .UnattachedDocComment, + .same_line_doc_comment, + .same_line_doc_comment, + .unattached_doc_comment, + .same_line_doc_comment, + .same_line_doc_comment, + .unattached_doc_comment, }); } @@ -3678,10 +3678,10 @@ test "zig fmt: hexadeciaml float literals with underscore separators" { } test "zig fmt: C var args" { - try testCanonical( - \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; - \\ - ); + try testCanonical( + \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + \\ + ); } //test "zig fmt: Only indent multiline string literals in function calls" { @@ -4037,8 +4037,8 @@ test "recovery: top level" { \\test "" {inline} \\test "" {inline} , &[_]Error{ - .ExpectedInlinable, - .ExpectedInlinable, + .expected_inlinable, + .expected_inlinable, }); } @@ -4049,8 +4049,8 @@ test "recovery: block statements" { \\ inline; \\} , &[_]Error{ - .InvalidToken, - .ExpectedInlinable, + .invalid_token, + .expected_inlinable, }); } @@ -4066,10 +4066,10 @@ test "recovery: block statements" { // \\ } // \\} // , &[_]Error{ -// .ExpectedToken, -// .ExpectedToken, -// .InvalidAnd, -// .InvalidToken, +// .expected_token, +// .expected_token, +// .invalid_and, +// .invalid_token, // }); //} @@ -4078,8 +4078,8 @@ test "recovery: extra qualifier" { \\const a: *const const u8; \\test "" , &[_]Error{ - .ExtraConstQualifier, - .ExpectedLBrace, + .extra_const_qualifier, + .expected_block, }); } @@ -4091,8 +4091,8 @@ test "recovery: extra qualifier" { // \\test "" // , &[_]Error{ // .ExpectedReturnType, -// .InvalidAnd, -// .ExpectedLBrace, +// .invalid_and, +// .expected_block, // }); //} @@ -4105,10 +4105,10 @@ test "recovery: extra qualifier" { // \\ async a && b; // \\} // , &[_]Error{ -// .ExpectedToken, +// .expected_token, // .ExpectedPubItem, // .ExpectedParamList, -// .InvalidAnd, +// .invalid_and, // }); // try testError( // \\threadlocal test "" { @@ -4117,7 +4117,7 @@ test "recovery: extra qualifier" { // , &[_]Error{ // .ExpectedVarDecl, // .ExpectedParamList, -// .InvalidAnd, +// .invalid_and, // }); //} @@ -4126,13 +4126,13 @@ test "recovery: extra qualifier" { // \\inline test "" { a && b; } // , &[_]Error{ // .ExpectedFn, -// .InvalidAnd, +// .invalid_and, // }); // try testError( // \\extern "" test "" { a && b; } // , &[_]Error{ // .ExpectedVarDeclOrFn, -// .InvalidAnd, +// .invalid_and, // }); //} @@ -4144,12 +4144,12 @@ test "recovery: extra qualifier" { // \\ @foo // \\} // , &[_]Error{ -// .InvalidAnd, -// .ExpectedToken, -// .InvalidAnd, -// .ExpectedToken, +// .invalid_and, +// .expected_token, +// .invalid_and, +// .expected_token, // .ExpectedParamList, -// .ExpectedToken, +// .expected_token, // }); //} @@ -4163,12 +4163,12 @@ test "recovery: extra qualifier" { // \\ a && b // \\} // , &[_]Error{ -// .ExpectedExpr, -// .ExpectedToken, -// .ExpectedToken, -// .ExpectedContainerMembers, -// .InvalidAnd, -// .ExpectedToken, +// .expected_expr, +// .expected_token, +// .expected_token, +// .expected_container_members, +// .invalid_and, +// .expected_token, // }); //} @@ -4178,7 +4178,7 @@ test "recovery: extra qualifier" { // \\ a(comptime T: type) // \\} // , &[_]Error{ -// .ExpectedToken, +// .expected_token, // }); //} @@ -4189,10 +4189,10 @@ test "recovery: extra qualifier" { // \\ a && b; // \\} // , &[_]Error{ -// .ExpectedContainerMembers, -// .ExpectedContainerMembers, -// .ExpectedContainerMembers, -// .InvalidAnd, +// .expected_container_members, +// .expected_container_members, +// .expected_container_members, +// .invalid_and, // }); //} // @@ -4202,7 +4202,7 @@ test "recovery: mismatched bracket at top level" { \\ arr: 128]?G \\}; , &[_]Error{ - .ExpectedToken, + .expected_token, }); } @@ -4212,9 +4212,9 @@ test "recovery: mismatched bracket at top level" { // \\ error && foo; // \\} // , &[_]Error{ -// .ExpectedToken, +// .expected_token, // .ExpectedIdentifier, -// .InvalidAnd, +// .invalid_and, // }); //} @@ -4224,15 +4224,15 @@ test "recovery: mismatched bracket at top level" { // \\ var sequence = "repeat".*** 10; // \\} // , &[_]Error{ -// .AsteriskAfterPointerDereference, +// .asterisk_after_ptr_deref, // }); // try testError( // \\test "" { // \\ var sequence = "repeat".** 10&&a; // \\} // , &[_]Error{ -// .AsteriskAfterPointerDereference, -// .InvalidAnd, +// .asterisk_after_ptr_deref, +// .invalid_and, // }); //} @@ -4245,10 +4245,10 @@ test "recovery: mismatched bracket at top level" { // \\ a && b; // \\} // , &[_]Error{ -// .ExpectedSemiOrElse, -// .ExpectedSemiOrElse, -// .ExpectedSemiOrElse, -// .InvalidAnd, +// .expected_semi_or_else, +// .expected_semi_or_else, +// .expected_semi_or_else, +// .invalid_and, // }); //} @@ -4256,7 +4256,7 @@ test "recovery: invalid comptime" { try testError( \\comptime , &[_]Error{ - .ExpectedBlockOrField, + .expected_block_or_field, }); } @@ -4264,12 +4264,12 @@ test "recovery: missing block after for/while loops" { try testError( \\test "" { while (foo) } , &[_]Error{ - .ExpectedBlockOrAssignment, + .expected_block_or_assignment, }); try testError( \\test "" { for (foo) |bar| } , &[_]Error{ - .ExpectedBlockOrAssignment, + .expected_block_or_assignment, }); } @@ -4288,9 +4288,8 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b defer tree.deinit(allocator); for (tree.errors) |parse_error| { - const error_token = tree.errorToken(parse_error); - const token_start = tree.tokens.items(.start)[error_token]; - const loc = tree.tokenLocation(0, error_token); + const token_start = tree.tokens.items(.start)[parse_error.token]; + const loc = tree.tokenLocation(0, parse_error.token); try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); try tree.renderError(parse_error, stderr); try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); @@ -4362,7 +4361,7 @@ fn testCanonical(source: []const u8) !void { return testTransform(source, source); } -const Error = std.meta.Tag(std.zig.ast.Error); +const Error = std.zig.ast.Error.Tag; fn testError(source: []const u8, expected_errors: []const Error) !void { var tree = try std.zig.parse(std.testing.allocator, source); @@ -4370,6 +4369,6 @@ fn testError(source: []const u8, expected_errors: []const Error) !void { std.testing.expect(tree.errors.len == expected_errors.len); for (expected_errors) |expected, i| { - std.testing.expectEqual(expected, tree.errors[i]); + std.testing.expectEqual(expected, tree.errors[i].tag); } } diff --git a/src/Module.zig b/src/Module.zig index 35819c5d44..8f2ac3721e 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1723,7 +1723,7 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree { err_msg.* = .{ .src_loc = .{ .file_scope = root_scope, - .byte_offset = tree.tokens.items(.start)[parse_err.loc()], + .byte_offset = tree.tokens.items(.start)[parse_err.token], }, .msg = msg.toOwnedSlice(), }; diff --git a/src/main.zig b/src/main.zig index c0ac41d9bf..09d791cfb5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2898,7 +2898,7 @@ fn printErrMsgToFile( .on => true, .off => false, }; - const lok_token = parse_error.loc(); + const lok_token = parse_error.token; const token_starts = tree.tokens.items(.start); const token_tags = tree.tokens.items(.tag); -- cgit v1.2.3 From 70761d7c52cd3634e086b0914c9520ef4dc01eee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 19 Feb 2021 20:27:06 -0700 Subject: stage2: remove incorrect newlines from log statements --- src/Compilation.zig | 4 ++-- src/Module.zig | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index b3ee73f03f..227355de93 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1935,7 +1935,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { "o", &digest, cimport_zig_basename, }); if (comp.verbose_cimport) { - log.info("C import output: {s}\n", .{out_zig_path}); + log.info("C import output: {s}", .{out_zig_path}); } return CImportResult{ .out_zig_path = out_zig_path, @@ -2999,7 +2999,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { for (errors.list) |full_err_msg| { switch (full_err_msg) { .src => |src| { - log.err("{s}:{d}:{d}: {s}\n", .{ + log.err("{s}:{d}:{d}: {s}", .{ src.src_path, src.line + 1, src.column + 1, diff --git a/src/Module.zig b/src/Module.zig index 8f2ac3721e..9a918321c4 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -924,7 +924,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { .complete => return, .outdated => blk: { - log.debug("re-analyzing {s}\n", .{decl.name}); + log.debug("re-analyzing {s}", .{decl.name}); // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. @@ -1943,7 +1943,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { // Handle explicitly deleted decls from the source code. Not to be confused // with when we delete decls because they are no longer referenced. for (deleted_decls.items()) |entry| { - log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); + log.debug("noticed '{s}' deleted from source", .{entry.key.name}); try mod.deleteDecl(entry.key); } } @@ -2087,7 +2087,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { // not be present in the set, and this does nothing. decl.container.removeDecl(decl); - log.debug("deleting decl '{s}'\n", .{decl.name}); + log.debug("deleting decl '{s}'", .{decl.name}); const name_hash = decl.fullyQualifiedNameHash(); self.decl_table.removeAssertDiscard(name_hash); // Remove itself from its dependencies, because we are about to destroy the decl pointer. @@ -2189,18 +2189,18 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { defer inner_block.instructions.deinit(self.gpa); func.state = .in_progress; - log.debug("set {s} to in_progress\n", .{decl.name}); + log.debug("set {s} to in_progress", .{decl.name}); try zir_sema.analyzeBody(self, &inner_block, func.zir); const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); func.state = .success; func.body = .{ .instructions = instructions }; - log.debug("set {s} to success\n", .{decl.name}); + log.debug("set {s} to success", .{decl.name}); } fn markOutdatedDecl(self: *Module, decl: *Decl) !void { - log.debug("mark {s} outdated\n", .{decl.name}); + log.debug("mark {s} outdated", .{decl.name}); try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); if (self.failed_decls.swapRemove(decl)) |entry| { entry.value.destroy(self.gpa); -- cgit v1.2.3 From 5b597a16c6c4ac36a8d2004d5eeed62c38c75253 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 19 Feb 2021 20:57:06 -0700 Subject: stage2: fix not setting up ZIR arg instruction correctly This is a regression from when I briefly flirted with changing how arg ZIR instructions work in this branch, and then failed to revert it correctly. --- src/Module.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 9a918321c4..7af4648c79 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1340,13 +1340,15 @@ fn astgenAndSemaFn( const name_token = param.name_token.?; const src = token_starts[name_token]; const param_name = try mod.identifierTokenString(&gen_scope.base, name_token); - const arg = try decl_arena.allocator.create(zir.Inst.NoOp); + const arg = try decl_arena.allocator.create(zir.Inst.Arg); arg.* = .{ .base = .{ .tag = .arg, .src = src, }, - .positionals = .{}, + .positionals = .{ + .name = param_name, + }, .kw_args = .{}, }; gen_scope.instructions.items[i] = &arg.base; @@ -3929,7 +3931,9 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void /// Identifier token -> String (allocated in scope.arena()) pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { const tree = scope.tree(); + const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); + assert(token_tags[token] == .identifier); const ident_name = tree.tokenSlice(token); if (mem.startsWith(u8, ident_name, "@")) { -- cgit v1.2.3